哪个更有效,一个针对每个循环,还是一个迭代器?

2022-08-31 06:00:31

哪种是遍历集合的最有效方法?

List<Integer>  a = new ArrayList<Integer>();
for (Integer integer : a) {
  integer.toString();
}

List<Integer>  a = new ArrayList<Integer>();
for (Iterator iterator = a.iterator(); iterator.hasNext();) {
   Integer integer = (Integer) iterator.next();
   integer.toString();
}

请注意,这不是这个,这个这个这个的完全重复,尽管最后一个问题的答案之一很接近。这不是一个重复的原因是,其中大多数都是比较你在循环内调用的循环,而不是使用迭代器。get(i)

正如Meta上所建议的那样,我将发布我对这个问题的回答。


答案 1

如果您只是在集合上徘徊以读取所有值,那么使用迭代器或新的for循环语法之间没有区别,因为新语法只是在水下使用迭代器。

但是,如果您的意思是循环旧的“c-style”循环:

for(int i=0; i<list.size(); i++) {
   Object o = list.get(i);
}

然后,新的 for 循环或迭代器可以更加高效,具体取决于底层数据结构。这样做的原因是,对于某些数据结构,是O(n)操作,这使得循环成为O(n2)操作。传统的链表就是这种数据结构的一个例子。所有迭代器的基本要求都应该是O(1)操作,使循环成为O(n)。get(i)next()

要验证新的 for 循环语法是否在水下使用了迭代器,请比较从以下两个 Java 代码段中生成的字节码。首先是 for 循环:

List<Integer>  a = new ArrayList<Integer>();
for (Integer integer : a)
{
  integer.toString();
}
// Byte code
 ALOAD 1
 INVOKEINTERFACE java/util/List.iterator()Ljava/util/Iterator;
 ASTORE 3
 GOTO L2
L3
 ALOAD 3
 INVOKEINTERFACE java/util/Iterator.next()Ljava/lang/Object;
 CHECKCAST java/lang/Integer
 ASTORE 2 
 ALOAD 2
 INVOKEVIRTUAL java/lang/Integer.toString()Ljava/lang/String;
 POP
L2
 ALOAD 3
 INVOKEINTERFACE java/util/Iterator.hasNext()Z
 IFNE L3

第二,迭代器:

List<Integer>  a = new ArrayList<Integer>();
for (Iterator iterator = a.iterator(); iterator.hasNext();)
{
  Integer integer = (Integer) iterator.next();
  integer.toString();
}
// Bytecode:
 ALOAD 1
 INVOKEINTERFACE java/util/List.iterator()Ljava/util/Iterator;
 ASTORE 2
 GOTO L7
L8
 ALOAD 2
 INVOKEINTERFACE java/util/Iterator.next()Ljava/lang/Object;
 CHECKCAST java/lang/Integer
 ASTORE 3
 ALOAD 3
 INVOKEVIRTUAL java/lang/Integer.toString()Ljava/lang/String;
 POP
L7
 ALOAD 2
 INVOKEINTERFACE java/util/Iterator.hasNext()Z
 IFNE L8

如您所见,生成的字节代码实际上是相同的,因此使用任何一种形式都不会降低性能。因此,对于大多数人来说,您应该选择对您最有吸引力的循环形式,因为该循环的样板代码较少。


答案 2

区别不在于性能,而在于能力。当直接使用引用时,你有更多的权力来显式使用一种类型的迭代器(例如List.iterator()与List.listIterator(),尽管在大多数情况下它们返回相同的实现)。您还可以在循环中引用迭代器。这允许您执行诸如从集合中删除项目之类的操作,而无需获得 ConcurrentModificationException。

例如:

这没关系:

Set<Object> set = new HashSet<Object>();
// add some items to the set

Iterator<Object> setIterator = set.iterator();
while(setIterator.hasNext()){
     Object o = setIterator.next();
     if(o meets some condition){
          setIterator.remove();
     }
}

事实并非如此,因为它会引发并发修改异常:

Set<Object> set = new HashSet<Object>();
// add some items to the set

for(Object o : set){
     if(o meets some condition){
          set.remove(o);
     }
}