【发布时间】:2013-07-05 03:23:00
【问题描述】:
Java 5 为我们提供了 for-each 循环,应尽可能使用它。
但是如果你需要在块中使用数组的索引,什么是最有效的习语呢?
// Option (1)
for (int i = array.length - 1; i >= 0; i--) {
// Code.
}
// Option (2)
for (int i = 0; i < array.length; i++) {
// Code.
}
// Option (3)
for (int i = 0, n = array.length; i < n; i++) {
// Code.
}
(显然,这在大多数程序中不会产生很大的性能差异,但让我很高兴。):-)
向后循环,非常可怕。也许甚至缓存不友好?或者现代处理器可以检测到内存中的倒退吗?
更短,我可以看到 JIT 编译器如何确定
array永远不会改变,因此length是恒定的,因此它基本上可以用 (3) 替换它。但它会这样做吗? (假设 JVM 是 Oracle 的 Hotspot/Java 7。)由 Joshua Bloch 在 Effective Java 的第 45 项中建议作为最快的选项,如果它是一些
Collection.size()是上限。但它也适用于数组吗?从字节码(见下文)我可以看到每个周期保存一条arraylength指令(预优化)。
This question 关于 Dalvik 虚拟机中的 for 循环,将 (1)-(3) 列为最快到最慢。但是,这些信息来自 2008 年,而 Dalvik 今天已经成熟得多,所以我认为情况仍然如此。
看上面的例子生成的字节码,有明显的区别:
Compiled from "ForLoops.java"
public class ForLoops extends java.lang.Object{
static int[] array;
public ForLoops();
Code:
0: aload_0
1: invokespecial #10; //Method java/lang/Object."<init>":()V
4: return
public static void forLoop1();
Code:
0: getstatic #17; //Field array:[I
3: arraylength
4: iconst_1
5: isub
6: istore_0
7: goto 13
10: iinc 0, -1
13: iload_0
14: ifge 10
17: return
public static void forLoop2();
Code:
0: iconst_0
1: istore_0
2: goto 8
5: iinc 0, 1
8: iload_0
9: getstatic #17; //Field array:[I
12: arraylength
13: if_icmplt 5
16: return
public static void forLoop3();
Code:
0: iconst_0
1: istore_0
2: getstatic #17; //Field array:[I
5: arraylength
6: istore_1
7: goto 13
10: iinc 0, 1
13: iload_0
14: iload_1
15: if_icmplt 10
18: return
}
【问题讨论】:
-
理论上,1 和 3 比 2 更有效,在一般情况下,因为
array.length只需要评估一次。但是,在 Java 中评估length非常简单,尤其是在 JITCed 的情况下,因此理论上的差异在实践中几乎消失了。这很可能取决于循环体中的内容以及 JITC 优化器如何将循环控制与循环体融合。 -
@Nicholas 提取字节码做得很好!但重要的是要记住,字节码最终并不是由于 JIT 而被执行的。如果我们能得到 JIT 指令,那将 是最终答案,但这可能是不现实的,因为 (a) 它是特定于平台的,并且 (b) JIT 有不同的“级别”。另外,我不知道有任何 JIT 可以让运行时生成的代码具有任何透明度,至少在你自己在 JVM 内存空间中四处寻找的情况下是这样。
-
我当然应该指出,“最有效”的选择通常是最适合其余代码的选择。例如,向后迭代有时很尴尬,有时非常有用。同样,如果数组对象在循环内被替换,实际上可能需要在每次迭代中检查
array.length。 -
好点。但这个问题是关于许多人使用的典型案例。如果直截了当的解决方案是最有效的,那么很多人可以继续以他们的方式编写代码,并且心存平静(包括我自己)。
标签: java arrays performance for-loop