13

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

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

我的问题是:计算a.length(这里 a 是数组名称)成本高吗?如果不是,那么如何a.length在内部计算(意味着 JVM 如何确保 O(1) 访问它)?类似于:

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

即喜欢访问函数内部的局部变量的值。谢谢。

4

5 回答 5

15

我的问题是:计算 a.length 成本高吗

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

于 2013-08-01T15:34:08.273 回答
9

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

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

此处的潜在速度差以纳秒为单位(可能没有“s”)。

于 2013-08-01T15:34:15.813 回答
4

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

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

于 2013-08-01T15:34:58.987 回答
4

为了您的方便,我对它进行了微基准测试。编码:

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

总结:无明显变化。

于 2013-08-01T16:05:27.513 回答
2

在数组中,length不是一个函数,如List.size(). 当你在java中创建一个数组时,它的长度是一个常数。所以成本是最低的

于 2013-08-01T15:36:10.507 回答