我正在查看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++)
{
...........
...........
...........
}
即。比如在函数内部访问局部变量的值。感谢。
答案 0 :(得分:13)
我的问题是:计算a.length是否代价高昂
没有。它只是阵列上的一个字段(参见JLS section 10.7)。它并不昂贵,JVM知道它永远不会改变并且可以适当地优化循环。 (实际上,我希望一个好的JIT能够注意到用非负数初始化变量的正常模式,检查它是否小于length
然后访问数组 - 如果它注意到,它可以删除数组边界检查。)
答案 1 :(得分:8)
a.length
不是计算,而只是对数组中保存的字段的访问。这种类型的读取操作非常快。
如果代码是经常被调用的方法的一部分,那么几乎可以肯定JIT编译器会进行您建议的优化,以使其更快。
潜在的速度差异在这里以纳秒为单位(可能没有“s”)。
答案 2 :(得分:4)
是固定的。一旦声明它就无法在内存中更改该数组的大小(如果您尝试更改数组大小,它将在内存中创建一个新数组)。
因此我们得到O(1)长度查找。我们知道数组内存的总大小。如果我们也查看第一个索引的内存大小,我们可以快速计算以获得O(1)速度的长度。无论我们的数组有多大,它都需要花费相同的时间来查找内存中的大小并查找第一个索引的大小
答案 3 :(得分:3)
为方便起见,我对它进行了微观标记。代码:
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
o.s.ArrayLength.everyTime thrpt 1 3 5 40215.790 1490.800 ops/msec
o.s.ArrayLength.justOnce thrpt 1 3 5 40231.192 966.007 ops/msec
总结:没有可检测到的变化。
答案 4 :(得分:2)
在数组中,length
不是List.size()
中的函数。
在java中创建数组时,其长度是常量。因此成本很低