增强了循环性能

时间:2012-08-28 09:05:01

标签: java performance for-loop

我和我的朋友就此发生了争执。 请考虑以下代码段

for(i=0; i<someList.size(); i++) {
    //some logic
    }

此处someList.size()将在每次迭代时执行,因此建议将此大小计算迁移到循环之外(之前)。

现在当我使用像这样的扩展for循环时会发生什么,

for(SpecialBean bean: someBean.getSpecialList()) {
//some logic
}

是否有必要将someBean.getSpecialList()移到循环之外? 如果我要保留第二个片段,someBean.getSpecialList()会执行多少次?

5 个答案:

答案 0 :(得分:8)

重复调用list.size()不会导致任何性能损失。 JIT编译器最有可能内联它,即使它没有,它仍然会相当便宜,因为它只涉及读取字段的值。

第一个例子的一个更严重的问题是循环体必须涉及list.get(i)而对于LinkedList,进行 i 元素有O( i )由于指针追踪而成本具有非常显着的常数因子,这转化为CPU级别上与数据相关的负载。 CPU的预取器无法优化此访问模式。

这意味着当应用于LinkedList时,整体计算复杂度将为O(n 2 )。

您的第二个示例通过Iterator编译为迭代,并且仅评估someBean.getSpecialList().iterator()一次。 iterator.next()的费用在所有情况下都是不变的。

答案 1 :(得分:4)

来自Joshua Bloch的Effective Java中的第46项:

  

在1.5版中引入的for-each循环消除了混乱   以及隐藏迭代器或索引的错误机会   完全变量。由此产生的习语同样适用于   集合和数组:

     

//迭代集合和数组的首选习惯用法   (元素e:元素){       doSomething的(E);当你看到冒号(:)时,将其读作“in”。因此,上面的循环读作“为元素中的每个元素e”。   即使使用for-each循环也没有性能损失   对于数组。事实上,它可能会提供轻微的性能优势   在某些情况下,一个普通的for循环,因为它计算限制   数组索引只有一次。虽然你可以手工完成(第45项),   程序员并不总是这样做。

另见is-there-a-performance-difference-between-a-for-loop-and-a-for-each-loop

答案 2 :(得分:0)

for each变体将与以下相同

for (Iterator i = c.iterator(); i.hasNext(); ) {
doSomething((Element) i.next()); 
}

项目46:首选 - 每个循环到传统for循环的有效java

  

for-each循环提供了超越传统的强大优势   清晰度和防止错误的循环,没有性能损失。您   应该尽可能地使用它。

所以我的第一个猜测是错误的,使用for each循环内的函数没有惩罚。

答案 3 :(得分:0)

第一个代码段的替代方法是:

for(i=0, l=someList.size(); i<l; i++) {
    //some logic
}

关于for..each循环,对getSpecialList()的调用只会进行一次(您可以通过在方法中添加一些调试/日志来验证这一点。)

答案 4 :(得分:0)

由于扩展循环使用从Iterable中取出的迭代器,因此多次执行someBean.getSpecialList()是不可能或不明智的。将它移出循环不会改变循环的性能,但如果它提高了可读性,你就可以做到。

注意:如果您按索引进行迭代,则随机访问集合可能会更快,例如ArrayList,因为它不会创建迭代器,但对于不支持随机访问的索引集合来说速度较慢。