for-each vs for vs while

时间:2010-12-05 00:09:53

标签: java for-loop while-loop

我想知道在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(); ... }

4 个答案:

答案 0 :(得分:20)

#3有一个缺点,因为迭代器it的范围超出了循环的末尾。其他解决方案没有这个问题。

#2与#0完全相同,除了#0更易读,更不容易出错。

#1(可能)效率较低,因为每次循环都会调用.size()

#0通常是最好的,因为:

  • 这是最短的
  • 最不容易出错
  • 这是惯用的,容易让其他人一目了然
  • 它由编译器有效实现
  • 它不会使用不必要的名称污染您的方法范围(在循环外)

答案 1 :(得分:2)

简短回答是使用版本0.在Android's documentation for Designing for Performance处查看部分标题使用增强型循环语法。该页面有很多好东西,非常简洁明了。

答案 2 :(得分:1)

在我看来,#0是最容易阅读的,但#2和#3也同样适用。这三者之间应该没有性能差异。

几乎在任何情况下都不应该使用#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 Listjava.util。但是对于许多List实现来说,#1不是最理想的。实际上,对于LinkedListget(int)O(N)的列表,#1方法会导致O(N^2)列表迭代。

ArrayList的情况下,手动提升size()的调用,将其分配给(最终)局部变量是一件简单的事情。通过此优化,#1代码明显快于其他情况... ArrayList s。

关于在迭代元素时更改列表的观点引发了许多问题:

  • 如果使用显式或隐式使用迭代器的解决方案执行此操作,则根据列表类,您可能会获得ConcurrentModificationException s。如果您使用其中一个并发集合类,则不会获得异常,但是javadocs声明迭代器不一定会返回所有列表元素。

  • 如果您使用#1代码(按原样)执行此操作,那么您就遇到了问题。如果修改是由同一个线程执行的,则需要调整索引变量以避免丢失条目,或者将它们返回两次。即使你把所有事情都搞定了,也不会出现在当前位置之前同时插入的列表条目。

  • 如果#1情况下的修改是由不同的线程执行的,则很难正确同步。核心问题是get(int)size()是单独的操作。即使它们是单独同步的,也没有什么可以阻止其他线程在sizeget呼叫之间修改列表。

简而言之,迭代正在同时修改的列表很棘手,应该避免...... 除非你真的知道你在做什么