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
Collection.size()
的第 45 条中建议,如果它是上限的话,它是最快的选择。但它也适用于数组吗?从字节码(见下文)我可以看到arraylength
每个周期保存一条指令(预优化)。
这个关于 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
}