在遍历ArrayList,HashMap和其他集合时,是否有任何性能测试结果可用于比较传统的for循环与Iterator?
或者我为什么要将Iterator用于循环,反之亦然?
答案 0 :(得分:80)
假设这就是你的意思:
// 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),迭代器更快。对于数组和ArrayLists,性能差异应该可以忽略不计。
编辑:我认为微基准是非常邪恶的根源,就像早期优化一样。但话说回来,我觉得对这些相当琐碎的事情的影响感觉很好。因此我运行a small test:
除了“with with counter”与LinkedList之外的所有结果都相似。所有其他五个花费不到20毫秒来迭代整个列表。在LinkedList上使用list.get(i)
100,000次需要超过2分钟(!)才能完成(慢60,000次)。哇! :)因此,最好使用迭代器(显式或隐式使用每个),特别是如果你不知道你处理的列表的类型和大小。
答案 1 :(得分:22)
使用迭代器的第一个原因是显而易见的正确性。如果您使用手动索引,可能会出现非常无害的逐个错误,只有在您仔细观察时才能看到这些错误:您是从1开始还是从0开始?你在length - 1
结束了吗?您使用的是<
还是<=
?如果使用迭代器,则更容易看出它实际上正在迭代整个数组。 “说出你做了什么,做你说的话。”
第二个原因是对不同数据结构的统一访问。可以通过索引有效地访问数组,但最好通过记住访问的最后一个元素来遍历链表(否则您将获得“Shlemiel the painter”)。哈希映射更复杂。通过从这些和其他数据结构提供统一的接口(例如,您也可以进行树遍历),您再次获得明显的正确性。遍历逻辑只需实现一次,使用它的代码可以简洁地“说出它的作用,并按照它所说的去做。”
答案 2 :(得分:4)
大多数情况下表现相似。
然而,每当代码收到一个List并在其上循环时,就会出现众所周知的情况:
Iterator对于所有未实现RandomAccess的List实现更好(例如:LinkedList)。
原因是对于这些列表,按索引访问元素不是一个恒定的时间操作。
因此,您也可以将Iterator视为更强大(对于实现细节)。
与往常一样,性能不应隐藏可读性问题 java5 foreach循环在这方面是一个很大的打击: - )
答案 3 :(得分:2)
我不相信
for (T obj : collection) {
每次通过循环计算.size()因此比
更快for (int i = 0; i < collection.size(); i++) {
答案 4 :(得分:1)
对生成的代码使用JAD或JD-GUI,您会发现没有真正的区别。新迭代器形式的优点是它在代码库中看起来更干净。
编辑:我从其他答案中看到你实际上意味着使用get(i)和迭代器之间的区别。我把原始问题用来表示使用迭代器的旧方法和新方法之间的区别。
使用get(i)并维护自己的计数器,特别是对于List
类,不是一个好主意,原因在于接受的答案中提到的原因。
答案 5 :(得分:1)
使用迭代器而不是i ++语法的最佳理由之一是,并非所有数据结构都支持随机访问,更不用说让它运行良好。您还应该对列表或集合界面进行编程,这样如果您以后确定另一个数据结构更有效,您就可以在没有大规模手术的情况下将其交换掉。在这种情况下(编码到接口的情况),您不一定知道实现细节,并且将其推迟到数据结构本身可能更明智。
答案 6 :(得分:1)
我学会坚持使用每个原因的原因之一是它简化了嵌套循环,特别是超过2维循环。你可能最终操纵的所有i,j和k都会很快混淆。
答案 7 :(得分:1)
是的,它确实对基于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.
}
答案 8 :(得分:0)
+1 sfussenegger说的话。 FYI,无论是使用显式迭代器还是隐式迭代器(即每个)都不会产生性能差异,因为它们编译为相同的字节代码。