java中 .length的时间复杂度或隐藏成本

我在java中查看一个项目,发现了一个for循环,如下所示:

 for(int i=1; i<a.length; i++) { ........... ........... ........... } 

我的问题是:计算a.length (这里是数组名称)是否代价高昂? 如果没有那么a.length是如何在内部计算的(意味着JVM如何确保O(1)访问它)? 是类似于:

 int length = a.length; for(int i=1; i<length; i++) { ........... ........... ........... } 

就像访问函数内部的局部变量值一样。 谢谢。

我的问题是:计算a.length是否代价高昂

不。它只是arrays上的一个字段(参见JLS第10.7节 )。 它并不昂贵,JVM知道它永远不会改变并且可以适当地优化循环。 (实际上,我希望一个好的JIT能够注意到用非负数初始化变量的正常模式,检查它是否小于length然后访问数组 – 如果它注意到,它可以删除数组边界检查。 )

a.length不是计算,而只是访问arrays中保存的字段。 这种类型的读操作非常快。

如果代码是经常被调用的方法的一部分,那么几乎可以肯定JIT编译器会进行您建议的优化,以使其更快。

潜在的速度差在这里是纳秒(可能没有“s”)。

在java数组中是固定的。 一旦声明它就无法在内存中更改该数组的大小(如果您尝试更改数组大小,它将在内存中创建一个新数组)。

因此,我们得到O(1)长度查找。 我们知道数组内存的总大小。 如果我们也查看第一个索引的内存大小,我们可以快速计算以获得O(1)速度的长度。 无论我们的数组有多大,它都需要相同的时间来查找内存中的大小并查找第一个索引的大小

为方便起见,我对它进行了微观标记。 代码:

 public class ArrayLength { static final boolean[] ary = new boolean[10_000_000]; static final Random rnd = new Random(); @GenerateMicroBenchmark public void everyTime() { int sum = rnd.nextInt(); for (int i = 0; i < ary.length; i++) sum += sum; } @GenerateMicroBenchmark public void justOnce() { int sum = rnd.nextInt(); final int length = ary.length; for (int i = 0; i < length; i++) sum += sum; } } 

结果:

 Benchmark Mode Thr Cnt Sec Mean Mean error Units osArrayLength.everyTime thrpt 1 3 5 40215.790 1490.800 ops/msec osArrayLength.justOnce thrpt 1 3 5 40231.192 966.007 ops/msec 

总结:没有可检测到的变化。

在数组中, length不是List.size()的函数。 在java中创建数组时,其长度是常量。 所以成本很低