Java 中传统 for 循环与迭代器/foreach 的性能

2022-08-31 15:51:46

在遍历 ArrayList、HashMap 和其他集合时,在比较传统的 for 循环与迭代器时,是否有任何性能测试结果?

或者简单地说,为什么我应该使用迭代器 over for loop,反之亦然?


答案 1

假设这是你的意思:

// traditional for loop
for (int i = 0; i < collection.size(); i++) {
  T obj = collection.get(i);
  // snip
}

// using iterator
Iterator<T> iter = collection.iterator();
while (iter.hasNext()) {
  T obj = iter.next();
  // snip
}

// using iterator internally (confirm it yourself using javap -c)
for (T obj : collection) {
   // snip
}

对于没有随机访问的集合(例如TreeSet,HashMap,LinkedList),迭代器更快。对于数组和数组列表,性能差异应该可以忽略不计。

编辑:我相信微基准测试是邪恶的根源,就像早期优化一样。但话又说回来,我认为对这种非常微不足道的事情的影响有一种感觉是件好事。因此,我运行了一个小测试

  • 以特定方式迭代链接列表和数组列表
  • 具有 100,000 个“随机”字符串
  • 总结它们的长度(只是为了避免编译器优化整个循环)
  • 使用所有 3 种循环样式(迭代器,对于每个,对于带计数器)

除了LinkedList的“for with counter”之外,所有结果都是相似的。所有其他五个都花了不到20毫秒的时间来迭代整个列表。在LinkedList上使用100,000次需要超过2分钟(!)才能完成(慢60,000倍)。哇!:)因此,最好使用迭代器(显式或隐式地用于每个迭代器),特别是如果您不知道要处理的列表的类型和大小。list.get(i)


答案 2

使用迭代器的第一个原因是明显的正确性。如果您使用手动索引,则可能存在非常无害的偏离一个错误,只有仔细观察才能看到这些错误:您是从1开始还是从0开始?你完成了吗?您是否使用 或 ?如果使用迭代器,则更容易看出它确实在迭代整个数组。“说你做什么,做你说的。length - 1<<=

第二个原因是对不同数据结构的统一访问。数组可以通过索引有效地访问,但最好通过记住最后访问的元素来遍历链表(否则你会得到一个“画家什勒米尔”)。哈希映射甚至更复杂。通过从这些和其他数据结构提供统一的接口(例如,您还可以进行树遍历),您可以再次获得明显的正确性。遍历逻辑只需要实现一次,使用它的代码可以简洁地“说出它所做的事情,并按照它所说的去做”。