在为我们的应用程序中的每个循环更新for循环时,我遇到了很多这些“模式”:
for (int i = 0, n = a.length; i < n; i++) {
...
}
而不是
for (int i = 0; i < a.length; i++) {
...
}
我可以看到您获得了集合的性能,因为您不需要为每个循环调用 size()方法。但是对于数组??
所以问题出现了:array.length
比常规变量更贵吗?
答案 0 :(得分:68)
不,对array.length
的调用是O(1)
或持续时间操作。
由于.length
是public
final
成员array
,因此访问权限并不比本地变量慢。 (与调用size()
)
现代JIT编译器可能无论如何都会优化对.length
的调用。
您可以通过查看OpenJDK中JIT编译器的源代码,或通过让JVM转储出JIT编译的本机代码并检查代码来确认这一点。
请注意,可能存在JIT编译器无法执行此操作的情况;例如
答案 1 :(得分:15)
午饭时我有点时间:
public static void main(String[] args) {
final int[] a = new int[250000000];
long t;
for (int j = 0; j < 10; j++) {
t = System.currentTimeMillis();
for (int i = 0, n = a.length; i < n; i++) { int x = a[i]; }
System.out.println("n = a.length: " + (System.currentTimeMillis() - t));
t = System.currentTimeMillis();
for (int i = 0; i < a.length; i++) { int x = a[i]; }
System.out.println("i < a.length: " + (System.currentTimeMillis() - t));
}
}
结果:
n = a.length: 672
i < a.length: 516
n = a.length: 640
i < a.length: 516
n = a.length: 656
i < a.length: 516
n = a.length: 656
i < a.length: 516
n = a.length: 640
i < a.length: 532
n = a.length: 640
i < a.length: 531
n = a.length: 641
i < a.length: 516
n = a.length: 656
i < a.length: 531
n = a.length: 656
i < a.length: 516
n = a.length: 656
i < a.length: 516
注意:
n = a.length
显示比i < a.length
快约一半,可能是由于垃圾收集(?)。250000000
OutOfMemoryError
270000000
,我无法让{{1}}更大。关键是,并且它是其他人一直在制作的,你必须运行Java内存,你仍然没有看到两种选择之间的速度有显着差异。把你的开发时间花在真正重要的事情上。
答案 2 :(得分:11)
我怀疑是否有任何重大差异,即使有,我敢打赌它可能在编译期间被优化掉了。当你尝试微观优化这样的事情时,你会浪费你的时间。首先使代码可读和正确,然后如果遇到性能问题,请使用分析器,然后担心选择更好的数据结构/算法(然后>担心优化您的探查器突出显示的部分。
答案 3 :(得分:7)
数组的长度存储为Java中数组的成员变量(与元素不同),因此获取该长度是一个常量时间操作,与从常规类中读取成员变量相同。许多较旧的语言(如C和C ++)不会将长度存储为数组的一部分,因此您需要在循环开始之前存储长度。你不必在Java中这样做。
答案 4 :(得分:4)
在这种情况下,为什么不进行反向循环:
for (int i = a.length - 1; i >= 0; --i) {
...
}
这里有2个微优化:
答案 5 :(得分:3)
将它存储在变量中可能会稍快一些。但如果一个分析器指出它是一个问题,我会非常惊讶。
在字节码级别,使用arraylength
字节码完成数组的长度。我不知道它是否比iload
字节码慢,但是应该没有足够的区别来注意。
答案 6 :(得分:2)
这个答案来自C#的观点,但我认为这同样适用于Java。
在C#中,成语
for (int i = 0; i < a.length; i++) { ...}
被认为是在数组上进行迭代,因此在循环中访问数组而不是每次访问数组时都会避免边界检查。
这可能会或可能不会被以下代码识别:
for (int i = 0, n = a.length; i < n; i++) { ...}
或
n = a.length;
for (int i = 0; i < n; i++) { ...}
编译器与我不知道的JITter执行了多少此优化,特别是如果它由JITter执行,我希望所有3都生成相同的本机代码。
然而,第一种形式也可以说是人们更具可读性,所以我想说的就是这样。
答案 7 :(得分:1)
Array.length是一个常量,JIT编译器应该在两个实例中都看到它。我希望在这两种情况下得到的机器代码都是相同的。至少对于服务器编译器而言。