for-each 与 for 与 while
我想知道在 ArrayList 或每种列表上实现“for-each”循环的最佳方法是什么。
以下哪种实现最好,为什么?或者有没有最好的办法?
感谢您的帮助。
List values = new ArrayList();
值.add(“一”);
值.add(“两个”);
值.add(“三”);
...
//#0
for(字符串值:值){
...
}
//#1
for(int i = 0; i
//#2
for(迭代器 it = value.iterator(); it.hasNext(); ) {
字符串值 = it.next();
...
}
//#3
迭代器 it = value.iterator();
while (it.hasNext()) {
字符串值 = (String) it.next();
...
}
I wonder what is the best way to implement a "for-each" loop over an ArrayList or every kind of List.
Which of the followings implementations is the best and why? Or is there a best way?
Thank you for your help.
List values = new ArrayList();
values.add("one"); values.add("two"); values.add("three"); ...
//#0
for(String value : values) { ... }
//#1
for(int i = 0; i < values.size(); i++) { String value = values.get(i); ... }
//#2
for(Iterator it = values.iterator(); it.hasNext(); ) { String value = it.next(); ... }
//#3
Iterator it = values.iterator(); while (it.hasNext()) { String value = (String) it.next(); ... }
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
#3 有一个缺点,因为迭代器
it
的范围超出了循环末尾。其他解决方案不存在此问题。#2 与 #0 完全相同,只是 #0 更具可读性且不易出错。
#1(可能)效率较低,因为它每次循环都会调用
.size()
。#0 通常是最好的,因为:
#3 has a disadvantage because the scope of the iterator
it
extends beyond the end of the loop. The other solutions don't have this problem.#2 is exactly the same as #0, except #0 is more readable and less prone to error.
#1 is (probably) less efficient because it calls
.size()
every time through the loop.#0 is usually best because:
简短的答案是使用版本 0。查看 Android 性能设计文档。该页面有很多好东西,并且非常清晰简洁。
The short answer is to use version 0. Take a peek at the section title Use Enhanced For Loop Syntax at Android's documentation for Designing for Performance. That page has a bunch of goodies and is very clear and concise.
在我看来,#0 是最容易阅读的,但#2 和#3 也同样有效。这三者之间应该没有性能差异。
几乎在任何情况下都不应该使用#1。您在问题中指出您可能想要迭代“每种列表”。如果您碰巧在
LinkedList
上进行迭代,那么 #1 将是 n^2 复杂度:不好。即使您绝对确定您使用的列表支持高效的随机访问(例如ArrayList
),通常也没有理由使用#1 而不是其他任何列表。#0 is the easiest to read, in my opinion, but #2 and #3 will work just as well. There should be no performance difference between those three.
In almost no circumstances should you use #1. You state in your question that you might want to iterate over "every kind of List". If you happen to be iterating over a
LinkedList
then #1 will be n^2 complexity: not good. Even if you are absolutely sure that you are using a list that supports efficient random access (e.g.ArrayList
) there's usually no reason to use #1 over any of the others.针对OP的这一评论。
让我们看看这些问题:
对于
List
合约的所有实现来说,get(int)
确实不是O(1)
。但是,据我所知,对于java.util
中的所有List
实现,size()
都是O(1)
。但你是对的,#1 对于许多List
实现来说并不是最优的。事实上,对于像LinkedList
这样的列表,其中get(int)
是O(N)
,#1 方法会导致O( N^2)
列表迭代。在 ArrayList 情况下,手动提升对 size() 的调用并将其分配给(最终)局部变量是一件简单的事情。通过这种优化,对于 ArrayList,#1 代码比其他情况要快得多。
您关于在迭代元素时更改列表的观点引发了许多问题:
如果您使用显式或隐式使用迭代器的解决方案执行此操作,则根据列表类,您可能会得到
ConcurrentModificationException
s。如果您使用并发集合类之一,则不会出现异常,但 javadoc 声明迭代器不一定会返回所有列表元素。如果您使用#1 代码(按原样)执行此操作,则会遇到问题。如果修改是由同一个线程执行的,则需要调整索引变量以避免丢失条目或返回两次。即使一切都正确,在当前位置之前同时插入的列表条目也不会显示。
如果#1情况下的修改是由不同的线程执行的,则很难正确同步。核心问题是
get(int)
和size()
是单独的操作。即使它们是单独同步的,也没有什么可以阻止其他线程在size
和get
调用之间修改列表。简而言之,迭代正在同时修改的列表是很棘手的,应该避免......除非您真的知道自己在做什么。
In response to this comment from the OP.
Lets look at these issues:
It is certainly true that
get(int)
is notO(1)
for all implementations of theList
contract. However, AFAIK,size()
isO(1)
for allList
implementations injava.util
. But you are correct that #1 is suboptimal for manyList
implementations. Indeed, for lists likeLinkedList
whereget(int)
isO(N)
, the #1 approach results in aO(N^2)
list iteration.In the
ArrayList
case, it is a simple matter to manually hoist the call tosize()
, assigning it to a (final) local variable. With this optimization, the #1 code is significantly faster than the other cases ... forArrayList
s.Your point about changing the list while iterating the elements raises a number of issues:
If you do this with a solution that explicitly or implicitly uses iterators, then depending on the list class you may get
ConcurrentModificationException
s. If you use one of the concurrent collection classes, you won't get the exception, but the javadocs state that the iterator won't necessarily return all of the list elements.If you do this using the #1 code (as is) then, you have a problem. If the modification is performed by the same thread, you need to adjust the index variable to avoid missing entries, or returning them twice. Even if you get everything right, a list entry concurrently inserted before the current position won't show up.
If the modification in the #1 case is performed by a different thread, it hard to synchronize properly. The core problem is that
get(int)
andsize()
are separate operations. Even if they are individually synchronized, there is nothing to stop the other thread from modifying the list between asize
andget
call.In short, iterating a list that is being concurrently modified is tricky, and should be avoided ... unless you really know what you are doing.