Java 中传统 for 循环与 Iterator/foreach 的性能

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1879255/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 23:28:28  来源:igfitidea点击:

Performance of traditional for loop vs Iterator/foreach in Java

javamapiteratorarraylistfor-loop

提问by Harish

Is there any performance testing results available in comparing traditional for loop vs Iterator while traversing a ArrayList,HashMap and other collections?

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

Or simply why should I use Iterator over for loop or vice versa?

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

采纳答案by sfussenegger

Assuming this is what you meant:

假设这是你的意思:

// 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
}

Iterator is faster for collections with no random access (e.g. TreeSet, HashMap, LinkedList). For arrays and ArrayLists, performance differences should be negligible.

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

Edit: I believe that micro-benchmarking is root of pretty much evil, just like early optimization. But then again, I think it's good to have a feeling for the implications of such quite trivial things. Hence I've run a small test:

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

  • iterate over a LinkedList and an ArrayList respecively
  • with 100,000 "random" strings
  • summing up their length (just something to avoid that compiler optimizes away the whole loop)
  • using all 3 loop styles (iterator, for each, for with counter)
  • 分别迭代 LinkedList 和 ArrayList
  • 有 100,000 个“随机”字符串
  • 总结它们的长度(只是为了避免编译器优化整个循环)
  • 使用所有 3 种循环样式(迭代器、for each、for with counter)

Results are similar for all but "for with counter" with LinkedList. All the other five took less than 20 milliseconds to iterate over the whole list. Using list.get(i)on a LinkedList 100,000 times took more than 2 minutes (!) to complete (60,000 times slower). Wow! :) Hence it's best to use an iterator (explicitly or implicitly using for each), especially if you don't know what type and size of list your dealing with.

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

回答by Paul Wagland

Use JADor JD-GUIagainst your generated code, and you will see that there is no real difference. The advantage of the new iterator form is that it looks cleaner in your codebase.

对生成的代码使用JADJD-GUI,您将看到没有真正的区别。新迭代器形式的优点是它在您的代码库中看起来更简洁。

Edit: I see from the other answers that you actually meant the difference between using get(i) versus an iterator. I took the original question to mean the difference between the old and new ways of using the iterator.

编辑:我从其他答案中看到,您实际上是指使用 get(i) 与迭代器之间的区别。我认为原始问题的意思是使用迭代器的新旧方法之间的区别。

Using get(i) and maintaining your own counter, especially for the Listclasses is not a good idea, for the reasons mentioned in the accepted answer.

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

回答by KLE

Performance is similar in most cases.

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

However, whenever a code receives a List, and loops on it, there is well-known case:
the Iterator is way better for all List implementations that do not implement RandomAccess(example: LinkedList).

但是,每当代码接收到一个 List 并在其上循环时,就会出现一个众所周知的情况:
对于所有未实现 RandomAccess 的 List 实现(例如:LinkedList),Iterator 的效果要好得多

The reason is that for these lists, accessing an element by index is not a constant time operation.

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

So you can also consider the Iterator as more robust (to implementation details).

所以你也可以认为迭代器更健壮(实现细节)。



As always, performance should not be hide readability issues.
The java5 foreach loop is a big hit on that aspect :-)

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

回答by Jason Tholstrup

One of the best reasons to use an iterator over the i++ syntax is that not all data structures will support random access let alone have it perform well. You should also be programming to the list or collection interface so that if you later decided that another data structure would be more efficient you'd be able to swap it out without massive surgery. In that case (the case of coding to an interface) you won't necessarily know the implementation details and it's probably wiser to defer that to the data structure itself.

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

回答by Ashton K

One of the reasons I've learned to stick with the for each is that it simplifies nested loops, especially over 2+ dimensional loops. All the i's, j's, and k's that you may end up manipulating can get confusing very quickly.

我学会坚持使用 for each 的原因之一是它简化了嵌套循环,尤其是超过 2 维循环。您最终可能会操纵的所有 i、j 和 k 很快就会变得混乱。

回答by Svante

The first reason to use an iterator is obvious correctness. If you use a manual index, there may be very innocuous off-by-one errors that you can only see if you look very closely: did you start at 1 or at 0? Did you finish at length - 1? Did you use <or <=? If you use an iterator, it is much easier to see that it is really iterating the whole array. "Say what you do, do what you say."

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

The second reason is uniform access to different data structures. An array can be accessed efficiently through an index, but a linked list is best traversed by remembering the last element accessed (otherwise you get a "Shlemiel the painter"). A hashmap is even more complicated. By providing a uniform interface from these and other data structures (e.g., you can also do tree traversals), you get obvious correctness again. The traversing logic has to be implemented only once, and the code using it can concisely "say what it does, and do what it says."

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

回答by erturne

+1 to what sfussenegger said. FYI, whether you use an explicit iterator or an implicit one (i.e. for each) won't make a performance difference because they compile to the same byte code.

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

回答by MeBigFatGuy

I don't believe that

我不相信

for (T obj : collection) {

calculates .size() each time thru the loop and is therefore faster than

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

for (int i = 0; i < collection.size(); i++) {

回答by mickeymoon

Yes, it does make a difference on collections which are not random access based like LinkedList. A linked list internally is implemented by nodes pointing to the next(starting at a head node).

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

The get(i) method in a linked list starts from the head node and navigates through the links all the way to the i'th node. When you iterate on the linked list using a traditional for loop, you start again from the head node each time, thus the overall traversal becomes quadratic time.

链表中的 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
}

While the for each loop iterates over the iterator obtained from the linked list and calls its next() method. The iterator maintains the states of the last access and thus does not start all the way from head everytime.

而 for each 循环遍历从链表获得的迭代器并调用其 next() 方法。迭代器维护上次访问的状态,因此不会每次都从头开始。

for( Object item: list ) {
    //item element is obtained from the iterator's next method.
}