【问题标题】:Visit every couple of element of a generic List in Java only once只访问 Java 中泛型 List 的每个元素一次
【发布时间】:2016-02-05 14:20:02
【问题描述】:

我有一个 Java 列表,我想利用对每个元素的访问一次。例如,如果我有一个数组,我会这样做:

O[] x = ...;
for(int i = 0; i<x.length; i++){
     for(int j=i+1; j<x.length;j++){
          someOperation(x[i],x[j]);
     }
}

问题是我有一个 List(我们假设不知道 List 是 ArrayList 还是 LinkedList)。 为了具有与前面列出的情况相同的复杂性,我会用伪代码编写,例如:

 ListIterator<O> it1 = list.listIterator();
 while(it1.hasNext()){
      O x = it1.next();
      it2 = it1.clone();   //it2 have the same "status" of it1, but it is a different object in memory
      while(it2.hasNext()){
           y= it.next();
           someOperation(x,y);
      }
 }

据我所知,我们没有类似 it1.clone() 的东西。做类似事情的唯一方法或多或少:

 int i = it1.nextIndex();
 it2 = list.listIterator(i);

但是,据我所知,

 list.listIterator(i);

的复杂度可能为 O(n) - 在 LinkedList 的情况下,这在其他语言中是绝对可以避免的。另一方面,使用随机访问(如 list.get(i))实现相同的算法会更糟糕。 假设列表是 LinkedList,编写代码的正确方法是什么?

【问题讨论】:

  • 您可以将LinkedList 复制到一个ArrayList 的开头,即O(n)。然后用它做你的配对操作。这是一个额外的 O(n),不会增加复杂性和一些额外的空间。
  • 如果你往另一个方向走怎么办。创建一个新列表并随时积累元素。
  • @BoristheSpider 你说得对,我错过了问题的那一部分。
  • @matt 我也喜欢你的方法。我认为它甚至比你的回答更好:) 实际上我第一次必须复制整个列表然后删除元素,而在这种方法中,我们只是在访问期间创建列表。没有任何过载..

标签: java list listiterator


【解决方案1】:

如果可以修改列表:

Iterator<Items> things = list.iterator();

while(things.hasNext()){
    Item item = things.next();
    things.remove();
    Iterator<Item> others = list.iterator();
    while(others.hasNext()){
        //... do stuff;
    }
}

如果顺序无关紧要,您可以构建一个新列表。

List<Item> others = new ArrayList<>(list.size());
for(Item item: list){
    for(Item other: others){
        // do stuff
    }
    others.add(item);
}

【讨论】:

  • 我认为这是解决问题的最佳方法,即使在这样做之前我必须复制列表。我很奇怪为什么我们没有像 iterator.clone() 这样的东西。在我看来会容易得多。
  • @BoristheSpider 我不同意你的观点,事实上 matt 在删除操作之后创建了第二个迭代器
  • @BoristheSpider 我想为每个使用一个,但我不确定。两个迭代器works。虽然,修改列表可能会比列表迭代器解决方案增加更多开销。
  • @matt 是的,它似乎确实有效 - 但如果 others 修改了 list,它显然会爆炸。
【解决方案2】:

你不能交换ij吗?

List<E> x = ...;
int j = 0;
for (E ej : x) {
    int iMax = j++;
    int i = 0;
    for (E ei : x) {
        if (i++ >= iMax)
            break;
        someOperation(ei, ej);
    }
}

【讨论】:

  • 我认为这是迄今为止最有效的解决方案,甚至是更简单的解决方案:不涉及迭代器,并且不需要额外的内存:) 谢谢
  • @Samuele 不客气。顺便说一句,迭代器隐含在“for-each”循环中。
【解决方案3】:

我相信最好的解决方案还是使用list.listIterator(i)

对于 LinkedList,这是 O(n),但您的算法的复杂度仍然是 O(n^2)。就Big-Oh而言,它不会改变任何东西!

干杯

【讨论】:

  • 你说得对,就 Big-O 而言没有任何变化,但你的代码 - 如果是链表 - 将花费或多或少的两倍时间,在我看来,是不好。
猜你喜欢
  • 1970-01-01
  • 2017-11-09
  • 1970-01-01
  • 2015-04-29
  • 2021-08-22
  • 2018-08-30
  • 2012-04-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多