我想知道在ArrayList或各种List上实现“for-each”循环的最佳方法是什么。
以下哪项实施最好,为什么?或者有最好的方法吗?
感谢您的帮助。
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();
...
}
答案 0 :(得分:20)
#3有一个缺点,因为迭代器it
的范围超出了循环的末尾。其他解决方案没有这个问题。
#2与#0完全相同,除了#0更易读,更不容易出错。
#1(可能)效率较低,因为每次循环都会调用.size()
。
#0通常是最好的,因为:
答案 1 :(得分:2)
简短回答是使用版本0.在Android's documentation for Designing for Performance处查看部分标题使用增强型循环语法。该页面有很多好东西,非常简洁明了。
答案 2 :(得分:1)
几乎在任何情况下都不应该使用#1。您在问题中说明您可能希望迭代“各种列表”。如果你碰巧在LinkedList
上进行迭代,那么#1将是n ^ 2复杂性:不好。即使您完全确定使用支持高效随机访问的列表(例如ArrayList
),通常也没有理由使用#1而不是其他任何一个。
答案 3 :(得分:1)
回应OP的评论。
但是,更新时需要#1(如果不是仅仅改变当前项目或将结果构建为新列表)并且附带索引。由于List&lt;&gt;是一个ArrayList&lt;&gt;在这种情况下,get()(和size())是O(1),但对于所有List-contract类型都不一样。
让我们看看这些问题:
对于get(int)
合同的所有实现,O(1)
肯定不是List
。但是,对于size()
中的所有O(1)
实施,AFAIK List
为java.util
。但是对于许多List
实现来说,#1不是最理想的。实际上,对于LinkedList
等get(int)
为O(N)
的列表,#1方法会导致O(N^2)
列表迭代。
在ArrayList
的情况下,手动提升对size()
的调用,将其分配给(最终)局部变量是一件简单的事情。通过此优化,#1代码明显快于其他情况... ArrayList
s。
关于在迭代元素时更改列表的观点引发了许多问题:
如果使用显式或隐式使用迭代器的解决方案执行此操作,则根据列表类,您可能会获得ConcurrentModificationException
s。如果您使用其中一个并发集合类,则不会获得异常,但是javadocs声明迭代器不一定会返回所有列表元素。
如果您使用#1代码(按原样)执行此操作,那么您就遇到了问题。如果修改是由同一个线程执行的,则需要调整索引变量以避免丢失条目,或者将它们返回两次。即使你把所有事情都搞定了,也不会出现在当前位置之前同时插入的列表条目。
如果#1情况下的修改是由不同的线程执行的,则很难正确同步。核心问题是get(int)
和size()
是单独的操作。即使它们是单独同步的,也没有什么可以阻止其他线程在size
和get
呼叫之间修改列表。
简而言之,迭代正在同时修改的列表很棘手,应该避免...... 除非你真的知道你在做什么。