【问题标题】:For-each vs Iterator. Which will be the better optionFor-each 与迭代器。哪个会是更好的选择
【发布时间】:2013-09-01 18:13:36
【问题描述】:

考虑以下情况。

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 是不是一种不好的做法?

【问题讨论】:

标签: java foreach iterator


【解决方案1】:

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
}

【讨论】:

  • 当然,最好记住从您循环的集合中添加/删除项目不是一个好习惯,因为您突出显示的原因。
【解决方案2】:

区别主要在于语法糖,除了迭代器可以从它正在迭代的集合中删除项目。从技术上讲,增强的 for 循环允许您遍历任何可迭代的内容,其中至少包括集合和数组。

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

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

对比:

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

【讨论】:

    【解决方案3】:

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

    【讨论】:

    • 不能不会“将”导致 ConcurrentModificationException
    • 如果为每个内部使用 Iterator 进行增强,这里有一个困惑,为什么在迭代时元素删除有限制?
    • @shaun 因为你无权访问它:)
    【解决方案4】:

    【讨论】:

    • 为什么你认为使用 list.foreach() 更好?在这样的 feaeach() 方法中,您不能修改 extern var 并且必须将其复制为 final 或将其包装在数组中。
    【解决方案5】:

    简单的答案:不和不。

    在内部,for-each 循环创建了一个 Iterator 来遍历集合。

    显式使用Iterator 的优点是您可以访问Iterators 方法。

    【讨论】:

    • 但是为什么代码中不需要引入Iterator类呢?
    • @Dakatine 因为导入是由编译器隐式完成的。
    【解决方案6】:

    这里是简单的代码 sn-p,用于检查 For-eachIteratorfor 的性能,用于遍历 ArrayList&lt;String&gt;,在 Java 版本 8 上执行。

            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 (9ms) For-each (19ms) For (27ms)

    这里 Iterator 的性能最好For 的性能最低。然而For-each 的性能介于两者之间。

    【讨论】:

      【解决方案7】:

      如果你想替换列表中的项目,我会用 for 循环去老学校

      for (int nIndex=0; nIndex < list.size(); nIndex++) {
        Obj obj = (Obj) list.get(nIndex);
      
        // update list item
        list.set(nIndex, obj2);
      }
      

      【讨论】:

        【解决方案8】:

        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()并继续循环

        至于“为什么这个无用的循环没有从编译后的代码中得到优化?我们可以看到它对列表项没有任何作用”:嗯,你可以编写你的迭代代码这样@ 987654333@ 有副作用,或者说.hasNext() 有副作用或有意义的后果。

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

        因此,即使我们可以证明循环体中没有发生任何事情……但证明在我们迭代时没有发生任何有意义/后果性的事情会更加昂贵(难以处理?)。编译器必须将这个空循环体留在程序中。

        我们所能期望的最好的结果就是编译器警告。有趣的是javac -Xlint:all Whatever.java 确实没有警告我们这个空循环体。 IntelliJ IDEA 可以。诚然,我已将 IntelliJ 配置为使用 Eclipse 编译器,但这可能不是原因。

        【讨论】:

          猜你喜欢
          • 2011-01-07
          • 2015-05-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-01-10
          相关资源
          最近更新 更多