For-each vs Iterator。哪个是更好的选择

时间:2013-08-29 10:42:55

标签: java foreach iterator

请考虑以下情形。

List<String> list = new ArrayList<>();

现在我添加了此列表的String值。

我使用以下方法来查看列表中的每个元素。

选项一 - 使用for-each

for (String i : list) {
        System.out.println(i);
    } 

选项二 - 使用Iterator

Iterator it=list.iterator();
while (it.hasNext()){
   System.out.println(it.next());
}

我只想知道如果我使用for-each代替Iterator,是否有任何性能优势。现在在Java中使用Iterator也是一种不好的做法吗?

8 个答案:

答案 0 :(得分:85)

for-each是使用iterators的语法糖(方法2)。

如果需要在循环中修改集合,则可能需要使用iterators。第一种方法会抛出异常。

for (String i : list) {
    System.out.println(i);
    list.remove(i); // throws exception
} 

Iterator it=list.iterator();
while (it.hasNext()){
    System.out.println(it.next());
    it.remove(); // valid here
}

答案 1 :(得分:16)

差别主要是语法糖,除了Iterator可以从正在迭代的Collection中删除项目。从技术上讲,增强的for循环允许你遍历任何可迭代的东西,它至少包括集合和数组。

不要担心性能差异。这种微观优化是一种无关紧要的分心。如果您需要随时删除项目,请使用迭代器。否则for循环往往更常用,因为它们更具可读性,即:

for (String s : stringList) { ... }

VS

for (Iterator<String> iter = stringList.iterator(); iter.hasNext(); ) {
  String s = iter.next();
  ...
}

答案 2 :(得分:9)

for-each是一个高级循环结构。在内部,它创建一个迭代器并迭代Collection。在for-each构造上使用实际Iterator对象的唯一好处是,您可以使用Iterator的方法(如.remove())修改您的集合。迭代时不使用迭代器方法修改集合将产生ConcurrentModificationException.

答案 3 :(得分:3)

答案 4 :(得分:2)

简单答案:不,不。

for-each循环在内部创建Iterator来迭代整个集合。

明确使用Iterator的好处是您可以访问Iterator方法。

答案 5 :(得分:0)

如果您想要替换列表中的项目,我会带着for循环去旧学校

for (int nIndex=0; nIndex < list.size(); nIndex++) {
  Obj obj = (Obj) list.get(nIndex);

  // update list item
  list.set(nIndex, obj2);
}

答案 6 :(得分:0)

foreach无论如何都使用了引擎盖下的迭代器。它真的只是语法糖。

考虑以下计划:

import java.util.List;
import java.util.ArrayList;

public class Whatever {
    private final List<Integer> list = new ArrayList<>();
    public void main() {
        for(Integer i : list) {
        }
    }
}

让我们用javac Whatever.java编译它,
并使用main()

读取javap -c Whatever的反汇编字节码
public void main();
  Code:
     0: aload_0
     1: getfield      #4                  // Field list:Ljava/util/List;
     4: invokeinterface #5,  1            // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
     9: astore_1
    10: aload_1
    11: invokeinterface #6,  1            // InterfaceMethod java/util/Iterator.hasNext:()Z
    16: ifeq          32
    19: aload_1
    20: invokeinterface #7,  1            // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
    25: checkcast     #8                  // class java/lang/Integer
    28: astore_2
    29: goto          10
    32: return

我们可以看到foreach编译成一个程序:

  • 使用List.iterator()
  • 创建迭代器
  • 如果Iterator.hasNext():调用Iterator.next()并继续循环

至于&#34;为什么这个无用的循环不会从编译的代码中优化出来?我们可以看到它没有对列表项做任何事情&#34;:嗯,您可以对您的迭代进行编码,使.iterator()有副作用,或者{ {1}}会产生副作用或产生有意义的后果。

您可以很容易地想象,表示来自数据库的可滚动查询的迭代可能会在.hasNext()上做一些戏剧性的事情(比如联系数据库,或关闭游标,因为您已达到结果集的末尾) )。

所以,尽管我们可以证明循环体中没有任何反应......但是,当我们迭代时,证明没有任何有意义/重要的事情会更加昂贵(难以处理?)。编译器必须将这个空循环体留在程序中。

我们希望的最好的是编译器警告。有趣的是.hasNext() 没有警告我们这个空循环体。但IntelliJ IDEA确实如此。不可否认,我已将IntelliJ配置为使用Eclipse编译器,但这可能不是原因。

enter image description here

答案 7 :(得分:0)

以下是一个简单的代码段,用于检查For-each vs Iterator vs for在Java版本8上执行ArrayList<String>遍历的性能。< /强>

        long MAX = 2000000;

        ArrayList<String> list = new ArrayList<>();

        for (long i = 0; i < MAX; i++) {

            list.add("" + i);
        }

        /**
         * Checking with for each iteration.
         */
        long A = System.currentTimeMillis();

        for (String data : list) {
            // System.out.println(data);
        }

        long B = System.currentTimeMillis();
        System.out.println(B - A + "ms");

        /**
         * Checking with Iterator method
         */

        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()) {
            iterator.next();
            // System.out.println(iterator.next());
        }

        long C = System.currentTimeMillis();
        System.out.println(C - B + "ms");

        /**
         * Checking with normal iteration.
         */
        for (int i = 0; i < MAX; i++) {
            list.get((int) (i % (MAX - i)));
            // System.out.println(list.get(i));
        }

        long D = System.currentTimeMillis();
        System.out.println(D - C + "ms");

平均输出值

19ms
9ms
27ms
  

结果分析:   Iterator(9毫秒)&lt; For-each(19毫秒)&lt; For(27ms)

     

此处 Iterator的效果最佳,而For的效果最差。但For-each表现介于两者之间。