map-J中传统for循环与Iterator / foreach的性能

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

或者只是为什么我应该在循环中使用Iterator,反之亦然?

Harish asked 2019-10-09T07:11:38Z
9个解决方案
80 votes

假设这是您的意思:

// 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),Iterator的速度更快。 对于数组和ArrayList,性能差异应该可以忽略不计。

编辑:我相信微基准测试是邪恶的根源,就像早期的优化一样。 但是话又说回来,我觉得对这样琐碎的事情的含义有感觉是一件好事。 因此,我进行了一个小测试:

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

除LinkedList“带有计数器”外,其他所有结果均相似。 所有其他五个在不到20毫秒的时间内遍历整个列表。 在LinkedList上使用list.get(i) 100,000次要花费超过2分钟(!)的时间(慢60,000倍)。 哇! :)因此,最好使用迭代器(显式或隐式地为每个迭代器使用),尤其是如果您不知道要处理的列表的类型和大小时。

sfussenegger answered 2019-10-09T07:12:38Z
22 votes

使用迭代器的第一个原因是显而易见的正确性。 如果您使用手动索引,那么如果仔细观察,可能会发现非常无害的一一错误:您是从1开始还是从0开始? 您完成了length - 1吗? 您是否使用<<=? 如果使用迭代器,则很容易看到它确实在迭代整个数组。 “说你做什么,说什么。”

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

Svante answered 2019-10-09T07:13:11Z
4 votes

在大多数情况下,性能是相似的。

但是,每当代码收到一个List并在其上循环时,就会发生一种众所周知的情况:
对于所有未实现RandomAccess的List实现(例如:LinkedList),Iterator都更好。

原因是对于这些列表,按索引访问元素不是固定时间的操作。

因此,您也可以将Iterator视为更健壮的(针对实现细节)。


与往常一样,性能不应成为隐藏可读性的问题。
java5 foreach循环在这方面很受欢迎:-)

KLE answered 2019-10-09T07:14:21Z
2 votes

我不相信

for (T obj : collection) {

每次循环都计算.size(),因此比

for (int i = 0; i < collection.size(); i++) {
MeBigFatGuy answered 2019-10-09T07:14:53Z
1 votes

在i ++语法上使用迭代器的最佳原因之一是,并非所有数据结构都将支持随机访问,更不用说它具有良好的性能了。 您还应该对列表或集合接口进行编程,这样,如果您以后决定使用另一个数据结构会更有效,则无需进行大量的手术就可以将其交换出来。 在那种情况下(对接口进行编码的情况),您不一定会知道实现细节,因此最好将其推迟到数据结构本身。

Jason Tholstrup answered 2019-10-09T07:15:19Z
1 votes

我学会坚持使用每个的原因之一是,它简化了嵌套循环,尤其是在2维以上的循环中。 您可能最终要操纵的所有i,j和k都会非常混乱。

Ashton K answered 2019-10-09T07:15:45Z
1 votes

对生成的代码使用JAD或JD-GUI,您将看到没有真正的区别。 新的迭代器表单的优点在于,它在您的代码库中看起来更干净。

编辑:我从其他答案中看到,您实际上意味着使用get(i)与迭代器之间的区别。 我以最初的问题来表示使用迭代器的新旧方法之间的区别。

由于接受的答案中提到的原因,使用get(i)并维护自己的计数器(尤其是List类)不是一个好主意。

Paul Wagland answered 2019-10-09T07:16:24Z
1 votes

是的,它确实对不是基于随机访问的集合(如LinkedList)产生了影响。 内部的链表是由指向下一个节点的节点(从头节点开始)实现的。

链接列表中的get(i)方法从头节点开始,并通过链接一直导航到第i个节点。 使用传统的for循环在链表上进行迭代时,每次都从头节点重新开始,因此总遍历变为二次时间。

for( int i = 0; i< list.size(); i++ ) {
    list.get(i); //this starts everytime from the head node instead of previous node
}

尽管for每个循环迭代从链表获得的迭代器,然后调用其next()方法。 迭代器维护最后一次访问的状态,因此不会每次都从头开始。

for( Object item: list ) {
    //item element is obtained from the iterator's next method.
}
mickeymoon answered 2019-10-09T07:17:05Z
0 votes

对sfussenegger说的话+1。 仅供参考,无论您使用显式迭代器还是隐式迭代器(即每个迭代器)都不会产生性能差异,因为它们可以编译为相同的字节码。

erturne answered 2019-10-09T07:17:30Z
translate from https://stackoverflow.com:/questions/1879255/performance-of-traditional-for-loop-vs-iterator-foreach-in-java