【问题标题】:Java foreach performanceJava foreach 性能
【发布时间】:2014-05-06 12:10:33
【问题描述】:

我在Java 应用程序中使用"foreach" 循环已有很长时间了。最近我开始想知道在性能(甚至结果)之间是否存在任何显着差异:

Collection<String> collection = someMethod();
for(String element : collection) { ... }

和:

for(String element : someMethod()) { ... }

如果不是Collection,而是ListMapSetarray,该怎么办?

【问题讨论】:

标签: java foreach


【解决方案1】:

两种代码之间的区别可以从它们生成的字节码中看出。

第一个比第二个多了两条指令;它将someMethod() 的返回值存储在本地堆栈上,然后再次加载它以访问它的迭代器。

但是第二个立即使用Iterator 而不将someMethod() 的返回值存储在本地堆栈中。

下面的代码显示了 3:4: 指令的消除。

第一:

Collection<String> collection = someMethod();
for(String element : collection) { ... }

     0: invokestatic  #3                  // Call someMethod()
     3: astore_1                          // Store the result as first item on local stack
     4: aload_1                           // Load the Collection again into operand stack
     5: invokeinterface #4,  1            // Get Iterator (of Collection) - InterfaceMethod java/util/Collection.iterator:()Ljava/util/Iterator;
    10: astore_2                          // Store Iterator on local stack #2
    11: aload_2                           // LOOP STARTS - Load iterator
    12: invokeinterface #5,  1            // InterfaceMethod java/util/Iterator.hasNext:()Z
    17: ifeq          33
    20: aload_2       
    21: invokeinterface #6,  1            // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
    26: checkcast     #7                  // class java/lang/String
    29: astore_3      
    30: goto          11                  // LOOP ENDS
    33: return

第二

for(String element : someMethod()) { ... }

     0: invokestatic  #3                  // Call someMethod() 
     3: invokeinterface #4,  1            // Get Iterator (of Collection) - Interface Method java/util/Collection.iterator:()Ljava/util/Iterator;/
     8: astore_1                          // Store Iterator on local stack #2  
     9: aload_1                           // LOOP STARTS - Load iterator
    10: invokeinterface #5,  1            // InterfaceMethod java/util/Iterator.hasNext:()Z
    15: ifeq          31
    18: aload_1       
    19: invokeinterface #6,  1            // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
    24: checkcast     #7                  // class java/lang/String
    27: astore_2      
    28: goto          9                   // LOOP ENDS
    31: return       

顺便说一句,我认为它们在性能方面不会有很大差异,因为它们在循环中都有 9 条指令。

【讨论】:

    【解决方案2】:

    两者之间没有明显的性能差异。第一种方式比第二种方式更优雅、更易读。但从性能的角度来看,它并不是一种威慑。两者都为“循环内部”部分生成相同的字节码。

    【讨论】:

      【解决方案3】:

      这是语义上的差异,由于将方法返回值分配给局部变量,您不会在代码中看到任何真正的性能变化。 Lists, Sets, Map entry sets, arrays在for-each循环中都是可迭代的,所以它们都很好,你不必知道返回集合的类型。

      【讨论】:

        【解决方案4】:

        为了兼容性,你应该使用第二个,如果你知道结果永远不是null

        如果您不再需要collection,则不应使用第一个。

        我更喜欢第三个建议:

        Collection<String> collection = someMethod();
        if (collection != null) {
            for(String element : collection) { ... }
        }
        

        问候。

        【讨论】:

        • 如果someMethod() 是我编写的,而不是某些第 3 方库的一部分,我通常更愿意返回一个空集合,以避免这些烦人的 null 检查。
        • 我真的很喜欢你成为我的同事。我能问你一件事吗?你如何看待assert collection != null;
        • 我认为在这些情况下(在迭代集合之前)使用断言是一个非常糟糕的主意,因为如果集合为空,我不希望我的程序终止。恕我直言,断言在测试中占有一席之地,而不是在生产代码中,但同样:这只是我的拙见。
        猜你喜欢
        • 2011-03-27
        • 2015-03-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多