5

为此,我和朋友发生了争执。考虑下面的片段,

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

每次迭代都会执行这里someList.size(),因此建议将此大小计算迁移到循环外部(之前)。

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

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

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

4

5 回答 5

8

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

您的第一个示例的一个更严重的问题是循环体必须涉及list.get(i),并且对于 a LinkedList,访问第i个元素具有 O( i ) 成本,并且由于指针追逐而具有相当显着的常数因子,这转化为数据依赖在 CPU 级别上加载。CPU 的预取器无法优化这种访问模式。

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

您的第二个示例通过编译为迭代,Iterator并且只会评估someBean.getSpecialList().iterator()一次。在所有情况下,成本iterator.next()都是恒定的。

于 2012-08-28T09:11:42.003 回答
4

来自 Joshua Bloch 在 Effective Java 中的第 46 条:

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

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

另请参阅is-there-a-performance-difference-between-a-for-loop-and-a-for-each-loop

于 2012-08-28T09:10:12.077 回答
0

第一个片段的替代方案是:

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

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

于 2012-08-28T09:09:59.713 回答
0

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

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

于 2012-08-28T09:11:34.010 回答
0

for each变化将与以下相同

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

来自第 46条:Effective java 的for-each 循环优于传统的 for 循环

与传统的 for 循环相比,for-each 循环在清晰度和错误预防方面提供了引人注目的优势,并且没有性能损失。你应该尽可能地使用它。

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

于 2012-08-28T09:07:37.223 回答