调用array.length的成本是多less?
在我们的应用程序中更新循环到for-each循环时,我遇到了很多这样的“模式”:
for (int i = 0, n = a.length; i < n; i++) { ... }
代替
for (int i = 0; i < a.length; i++) { ... }
我可以看到你获得集合的性能,因为你不需要在每个循环中调用size()方法。 但是用数组
所以问题出现了: array.length
比普通variables更昂贵吗?
不,对array.length
的调用是O(1)
或恒定时间操作。
由于.length
是array
的public
final
成员,因此访问速度不会慢于本地variables。 (这与调用size()
等方法非常不同)
一个现代的编译器可能无论如何都会优化对.length
的调用。
午餐时间我有点时间:
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
笔记:
- 如果反转testing,那么
n = a.length
显示为比i < a.length
快大约一半,可能是由于垃圾收集(?)造成的。 - 由于我在
270000000
处得到了OutOfMemoryError
,所以我不能做出250000000
的大。
重点是,这是其他人一直在做的,你必须运行Java内存,你仍然没有看到两个select之间的速度显着差异。 花费你的发展时间在重要的事情上。
我怀疑是否有任何显着的差异,即使有,我敢打赌,它可能在编译过程中优化。 当你试图微观地优化这样的事情时,你正在浪费你的时间。 首先让代码可读和正确,然后如果您遇到性能问题,请使用分析器 ,然后在适当的时候担心select更好的数据结构/algorithm, 然后担心优化探查器突出显示的部分。
在Java中,数组的长度作为数组的成员variables存储(与元素不一样),所以读取该长度是一个常量时间操作,就像读取普通类中的成员variables一样。 许多旧的语言,如C和C ++,不会将该长度存储为数组的一部分,因此您需要在循环开始之前存储该长度。 你不必在Java中这样做。
在那种情况下,你为什么不做一个逆循环:
for (int i = a.length - 1; i >= 0; --i) { ... }
这里有两个微观优化:
- 循环反转
- 后缀递减
将其存储在variables中可能会稍微快一点。 但如果一个分析人员指出这是一个问题,我会感到非常惊讶。
在字节码级别,用数组长度来获得数组的长度。 我不知道它是否比iload
字节码慢,但是不应该有足够的差别来注意。
这个答案是从C#的angular度来看,但我认为这同样适用于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++) { ...}
这个优化有多less是由编译器和我不知道的JITter执行的,特别是如果它是由JITter执行的,我希望所有3个都生成相同的本地代码。
然而,第一种forms也可以说是更可读的人,所以我会说去。
Array.length是一个常量,JIT编译器应该在两个实例中看透。 我希望在这两种情况下生成的机器代码是相同的。 至less为服务器编译器。