如何有效地对抽象列表进行嵌套迭代?

时间:2018-06-07 08:42:57

标签: java list iteration cartesian-product idiomatic

我试图复制“自我笛卡尔积”"的上对角矩阵的典型构造,以列表作为源。通俗地说,如果我有一个数组a,我想这样做:

for (int i = 0; i < a.length; i++) {
  for (int j = i; j < a.length; j++) {
    collect(a[i], a[j]);
  }
}

但是有一个抽象的List,我不能依靠有效的索引访问。

我已经考虑过这个问题,这是内存和时间效率的(因为调用sublist使用原始列表作为支持结构)但是看起来并不适合Java:

for (List<E> tail = list; !tail.isEmpty(); tail = tail.sublist(1, tail.size()) {
  E a = tail.get(0);
  for (E b : tail) {
    collect(a, b);
  }
}

有更好的选择吗?

样本:

如果输入序列为[1, 2, 3]且占位符 collect System.out.println,则输出应为其中的对(索引,而不是值) I&GT; = j的

1 1
1 2
1 3
2 2
2 3
3 3

所有可能的对(可以通过两个简单的循环完成)。

2 个答案:

答案 0 :(得分:2)

public static void collect(List<Integer> data) {
    List<Integer> a = data instanceof RandomAccess ? data : new ArrayList<>(data);

    for (int i = 0; i < a.size(); i++)
        for (int j = i; j < a.size(); j++)
            collect(a.get(i), a.get(j));
}

答案 1 :(得分:1)

您可以使用listIterator方法返回一个列表,该列表将返回将按顺序遍历您的列表的ListIterator对象。最重要的是,可以使用可选参数index调用该方法,以从列表中的给定点开始。此外,ListIterator方便地知道它的当前索引,我们可以用它来设置我们的第二个迭代器。

示例可能如下所示:

List<Integer> list = new LinkedList<Integer>(Arrays.asList(1, 2, 3));

ListIterator<Integer> i = list.listIterator();

while (i.hasNext())
{
    ListIterator<Integer> j = list.listIterator(i.nextIndex());
    int iV = i.next();
    while (j.hasNext())
    {
        collect(iV, j.next());
    }
}

为以下对调用collect

1, 1
1, 2
1, 3
2, 2
2, 3
3, 3

正如所提到的,这给我们留下了list.listIterator(i.nextIndex()) - 调用的潜在复杂性 O(n)

如果内存不是问题,那么另一个解决方案是确保您的List类型易于随机访问(例如,ArrayList等阵列支持的列表)并将数据复制到此类不应该是一个列表。