调用array.length的成本是多少

Str*_*kop 51 java arrays premature-optimization

在为我们的应用程序中的每个循环更新for循环时,我遇到了很多这些"模式":

for (int i = 0, n = a.length; i < n; i++) {
    ...
}
Run Code Online (Sandbox Code Playgroud)

代替

for (int i = 0; i < a.length; i++) {
    ...
}
Run Code Online (Sandbox Code Playgroud)

我可以看到你获得了集合的性能,因为你不需要为每个循环调用size()方法.但是有阵列?

所以问题出现了:array.length比常规变量更贵?

jjn*_*guy 69

不,呼叫array.lengthO(1)或恒定时间操作.

由于.lengthis(行为)是其public final成员array,因此访问本地变量并不慢.(这与调用方法非常不同size())

现代JIT编译器可能.length无论如何都会优化调用.

您可以通过查看OpenJDK中JIT编译器的源代码,或通过让JVM转储出JIT编译的本机代码并检查代码来确认这一点.

请注意,可能存在JIT编译器无法执行此操作的情况; 例如

  1. 如果您正在调试封闭方法,或者
  2. 如果循环体有足够的局部变量来强制寄存器溢出.

  • @jjnguy:对`array.length`的调用不能是O(1)并且仍然需要比`int len = array.length; 我<len;`,也是O(1)? (8认同)
  • @jjnguy:只是迂腐,并指出所有O(1)都不是平等的. (8认同)
  • 好吧,如果您担心这些事情,恕我直言,您应该使用 C 或 C++ 进行编程。(但是,是的,你可能是对的) (3认同)
  • "无论如何,现代编译器可能会优先调用`.length`." [引证需要] (3认同)

Gra*_*ner 17

午饭时我有点时间:

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));
    }
}
Run Code Online (Sandbox Code Playgroud)

结果:

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
Run Code Online (Sandbox Code Playgroud)

笔记:

  1. 如果你反转测试,那么n = a.length显示比i < a.length大约一半快,可能是由于垃圾收集(?).
  2. 250000000因为我得到了OutOfMemoryError,所以我不能做得更大270000000.

重点是,并且它是其他人一直在制作的,你必须运行Java内存,你仍然没有看到两个替代品之间的速度有显着差异.把你的开发时间花在真正重要的事情上.

  • 我有点失望的是,编译器没有意识到整个循环完全没有效果,只是简单地优化它. (4认同)

IRB*_*BMe 11

我怀疑是否有任何重大差异,即使有,我敢打赌它可能在编译期间被优化掉了.当你尝试微观优化这样的事情时,你会浪费你的时间.首先使代码可读和正确,然后如果遇到性能问题,请使用分析器,然后担心选择更好的数据结构/算法,然后担心优化探查器突出显示的部分.


Bil*_*ard 7

数组的长度存储为Java中数组的成员变量(与元素不同),因此获取该长度是一个常量时间操作,与从常规类中读取成员变量相同.许多较旧的语言(如C和C++)不会将长度存储为数组的一部分,因此您需要在循环开始之前存储长度.你不必在Java中这样做.

  • <nitpick>它不完全像一个成员变量; 它实际上有自己的特殊字节码.</挑剔> (4认同)