调用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)或恒定时间操作。

由于.lengtharraypublic 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 

笔记:

  1. 如果反转testing,那么n = a.length显示为比i < a.length快大约一半,可能是由于垃圾收集(?)造成的。
  2. 由于我在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为服务器编译器。