java 是否在循环中缓存数组长度计算
Does java cache array length calculation in loops
假设我有一个要迭代的数组:
int[] someArray = {1,2,3,4}
for (int i = 0; i < someArray.length; i++) {
// do stuff
}
每次迭代都会计算这个长度的数组,还是会优化为只计算一次?
我是否应该通过提前计算长度来迭代数组并将其传递给循环?
for (int i = 0, length = someArray.length; i < length ; i++) {
// do stuff
}
来自 JLS 7
10.7 数组成员
一个数组类型的成员都是以下几种:
• public final
字段 length
,其中包含组件的数量
阵列。长度可以是正数或零。
回到你的问题,java 没有重新计算 array.length
上数组中的元素数量。它 returns public final int length
的值,在数组创建期间计算。
因为 length
是 Array
的成员,所以当你创建一个数组时它已经被设置,在每次迭代中你只访问那个 属性 没有别的。
所以要么你像
一样访问它
int myArrayLength=arr.length;
for(int i=0;i<myArrayLength;i++)
或喜欢:
for(int i=0;i<arr.length;i++)
不会有可衡量的性能变化。
一如既往的性能:尽可能编写最简单的代码,并对其进行测试以查看其性能是否足够好。
如果您只需要元素(而不是索引),我建议您使用增强型 for 循环:
for (int value : array) {
...
}
根据 JLS 14.14.2 这基本上等同于您的第一段代码,但该代码仅讨论您真正感兴趣的内容。
但是,如果您 需要索引,并且假设您不更改 array
任何地方,我相信 JIT 编译器会优化本机代码以仅获取一次的长度。获取长度是一个 O(1) 操作,因为它基本上只是数组中的一个字段,但显然它确实涉及内存,因此 eventual 代码最好只执行此操作一次...但这并不意味着 您的 代码必须执行此操作。请注意,我不希望 Java 编译器 (javac
) 执行此优化 - 我希望 JIT 执行此优化。
事实上,我相信一个好的 JIT 实际上会看到这样的代码:
for (int i = 0; i < array.length; i++) {
int value = array[i];
...
}
并且能够优化数组边界检查——它可以识别如果它一直访问同一个数组对象,则不可能因数组边界错误而失败,因此它可以避免检查。它 可能 能够对预先获取长度的更多 "clever" 代码做同样的事情,但是 JIT 优化通常故意针对非常常见的代码模式(为了获得最大的"bang for buck") 并且上述遍历数组的方式非常常见。
假设我有一个要迭代的数组:
int[] someArray = {1,2,3,4}
for (int i = 0; i < someArray.length; i++) {
// do stuff
}
每次迭代都会计算这个长度的数组,还是会优化为只计算一次?
我是否应该通过提前计算长度来迭代数组并将其传递给循环?
for (int i = 0, length = someArray.length; i < length ; i++) {
// do stuff
}
来自 JLS 7
10.7 数组成员
一个数组类型的成员都是以下几种:
• public final
字段 length
,其中包含组件的数量
阵列。长度可以是正数或零。
回到你的问题,java 没有重新计算 array.length
上数组中的元素数量。它 returns public final int length
的值,在数组创建期间计算。
因为 length
是 Array
的成员,所以当你创建一个数组时它已经被设置,在每次迭代中你只访问那个 属性 没有别的。
所以要么你像
一样访问它 int myArrayLength=arr.length;
for(int i=0;i<myArrayLength;i++)
或喜欢:
for(int i=0;i<arr.length;i++)
不会有可衡量的性能变化。
一如既往的性能:尽可能编写最简单的代码,并对其进行测试以查看其性能是否足够好。
如果您只需要元素(而不是索引),我建议您使用增强型 for 循环:
for (int value : array) {
...
}
根据 JLS 14.14.2 这基本上等同于您的第一段代码,但该代码仅讨论您真正感兴趣的内容。
但是,如果您 需要索引,并且假设您不更改 array
任何地方,我相信 JIT 编译器会优化本机代码以仅获取一次的长度。获取长度是一个 O(1) 操作,因为它基本上只是数组中的一个字段,但显然它确实涉及内存,因此 eventual 代码最好只执行此操作一次...但这并不意味着 您的 代码必须执行此操作。请注意,我不希望 Java 编译器 (javac
) 执行此优化 - 我希望 JIT 执行此优化。
事实上,我相信一个好的 JIT 实际上会看到这样的代码:
for (int i = 0; i < array.length; i++) {
int value = array[i];
...
}
并且能够优化数组边界检查——它可以识别如果它一直访问同一个数组对象,则不可能因数组边界错误而失败,因此它可以避免检查。它 可能 能够对预先获取长度的更多 "clever" 代码做同样的事情,但是 JIT 优化通常故意针对非常常见的代码模式(为了获得最大的"bang for buck") 并且上述遍历数组的方式非常常见。