如果有的话,下面两个循环之间的性能差异是什么?

for (Object o: objectArrayList) {
    o.DoSomething();
}

and

for (int i=0; i<objectArrayList.size(); i++) {
    objectArrayList.get(i).DoSomething();
}

当前回答

不幸的是,两者之间似乎存在差异。

如果查看这两种循环生成的字节代码,就会发现它们是不同的。

下面是来自Log4j源代码的一个示例。

在/log4j-api/src/main/java/org/apache/ loggging/ log4j/ markermanager .java中,我们有一个名为Log4jMarker的静态内部类,它定义了:

    /*
     * Called from add while synchronized.
     */
    private static boolean contains(final Marker parent, final Marker... localParents) {
        //noinspection ForLoopReplaceableByForEach
        for (final Marker marker : localParents) {
            if (marker == parent) {
                return true;
            }
        }
        return false;
    }

使用标准循环:

  private static boolean contains(org.apache.logging.log4j.Marker, org.apache.logging.log4j.Marker...);
    Code:
       0: iconst_0
       1: istore_2
       2: aload_1
       3: arraylength
       4: istore_3
       5: iload_2
       6: iload_3
       7: if_icmpge     29
      10: aload_1
      11: iload_2
      12: aaload
      13: astore        4
      15: aload         4
      17: aload_0
      18: if_acmpne     23
      21: iconst_1
      22: ireturn
      23: iinc          2, 1
      26: goto          5
      29: iconst_0
      30: ireturn

for - each:

  private static boolean contains(org.apache.logging.log4j.Marker, org.apache.logging.log4j.Marker...);
    Code:
       0: aload_1
       1: astore_2
       2: aload_2
       3: arraylength
       4: istore_3
       5: iconst_0
       6: istore        4
       8: iload         4
      10: iload_3
      11: if_icmpge     34
      14: aload_2
      15: iload         4
      17: aaload
      18: astore        5
      20: aload         5
      22: aload_0
      23: if_acmpne     28
      26: iconst_1
      27: ireturn
      28: iinc          4, 1
      31: goto          8
      34: iconst_0
      35: ireturn

那个神谕是怎么回事?

我在Windows 7上用Java 7和Java 8尝试过。

其他回答

唯一确定的方法是对其进行基准测试,甚至这也不像听起来那么简单。JIT编译器可以对代码做一些意想不到的事情。

Foreach使你的代码的意图更清晰,这通常比非常小的速度改进更受欢迎——如果有的话。

每当我看到一个索引循环,我必须解析它一段时间,以确保它做什么,我认为它做,例如,它是否从零开始,它是否包括或排除终点等?

我的大部分时间似乎都花在了阅读代码(我写的或别人写的)上,而清晰度几乎总是比性能更重要。现在很容易忽略Hotspot的性能,因为Hotspot做得非常出色。

1. for(Object o: objectArrayList){
    o.DoSomthing();
}
and

2. for(int i=0; i<objectArrayList.size(); i++){
    objectArrayList.get(i).DoSomthing();
}

两者都做同样的事情,但为了方便和安全的编程使用,在第二种使用方式中有容易出错的可能性。

通过变量名称objectArrayList,我假设它是java.util.ArrayList的一个实例。在这种情况下,性能差异是不明显的。

另一方面,如果它是java.util的实例。LinkedList,第二种方法会慢得多,因为list# get(int)是一个O(n)操作。

因此,第一种方法总是首选的,除非循环中的逻辑需要索引。

在我看来,其他答案都是基于错误的基准测试,没有考虑Hotspot的编译和优化过程。

简短的回答

尽可能使用增强循环,因为大多数时候它是最快的。 如果不能,如果可能的话,将整个数组拉到一个局部变量中:

int localArray = this.array;
for (int i = 0; i < localArray.length; i++) { 
    methodCall(localArray[i]); 
}

长回答

现在,通常没有区别,因为Hotspot非常擅长优化和消除java需要做的检查。

但有时一些优化就是无法完成,通常是因为在循环中有一个虚拟调用,不能内联。 在这种情况下,有些循环确实比其他循环快。

Java需要做的一些事情:

重新加载。数组——因为它可以被改变(通过调用或另一个线程) 检查i是否在数组的边界内,如果不是抛出IndexOutOfBoundsException 检查被访问对象引用是否为空,如果是则抛出NullPointerException异常

考虑一下这个c风格的循环:

for (int i = 0; i < this.array.length; i++) { //now java knows i < this.array.length
    methodCall(this.array[i]);// no need to check
}

通过计算循环条件i < this.array。长度,Java知道I必须在边界内(I只在调用后更改),所以不需要在下一行中再次检查它。 但在这种情况下,java需要重新加载this.array.length。

你可能会想通过将this.array.length值拉到局部变量内部来“优化”循环:

int len = this.array.length;//len is now a local variable
for (int i = 0; i < len; i++) { //no reload needed
    methodCall(this.array[i]); //now java will do the check
}

现在java不需要每次都重新加载,因为局部变量可以被methodCall和/或另一个线程改变。局部变量只能在方法本身内部更改,java现在可以证明变量len不能更改。

但是现在循环条件i < this.array.length变为i < len,之前的优化失败,java需要检查i in是否在this.array的边界内。

一个更好的优化方法是将整个数组拉入一个局部变量:

ArrayType[] localArray = this.array;
for (int i = 0; i < localArray.length; i++) { 
    methodCall(localArray[i]); 
}

现在java不需要重新加载数组,“i in bounds”检查也被取消了。

那强化循环呢? 好吧,通常编译器会把增强的循环重写成类似上次显示的循环的东西,如果不是更好的话。