当我遍历List时,哪种方法最有效?

Chr*_*ris 3 java performance

List<T> list = new ArrayList<T>();
Run Code Online (Sandbox Code Playgroud)

1方法:

for(int i = list.length - 1; i >= 0; i--) {
  System.out.println(list.get(i));
}
Run Code Online (Sandbox Code Playgroud)

2方法:

for(T t : list) {
  System.out.println(t);
}
Run Code Online (Sandbox Code Playgroud)

3方法:

Iterator<T> it = list.iterator();     
while(it.hasNext()) {
  System.out.println(it.next());
}
Run Code Online (Sandbox Code Playgroud)

Jon*_*eet 12

效率不太可能显着 - 当然System.out.println更可能是您特定示例中的瓶颈.

第二种方法(增强的循环)是最可读的.请注意,这三种方法不会做同样的事情 - 第一种方法将从结束而不是从开始迭代.获得正确的行为几乎总是胜过少量,快一点.您的代码越可读,您就越有可能做到正确.

寻求可读性,衡量应用程序的性能,如果它成为一个问题,微观优化瓶颈(继续测量每一步).

编辑:我的答案是在问题的第一行,显示ArrayList<T>正在使用.

如果你想要一个答案任何 List<T>那么我们确实没有一个准确的答案.List<T>不提供任何有关复杂性的保证.它没有说明你应该有多快,也不会说get你期望迭代器的速度有多快.您可能正在使用具有良好随机访问权限的实现,但这是一个非常缓慢的迭代器.这不太可能,但它仍然是有效的List<T>.

简而言之:如果你担心效率,你需要知道你正在使用什么样的列表.通常,迭代它可能是合理有效的,并且随机访问可能有效也可能无效.(它可能比迭代更有效,但它不太可能显着提高效率.)在实践中,我的经验是,在大多数应用程序中,你实际上有足够的知识来做出合理的判断......但我通常仍会编写代码可读性首先.

正如其他人所指出的那样,如果你确实想使用随机访问来获取元素,那么确保你的列表也实现了RandomAccess接口是值得的.


jar*_*bjo 5

如果你有一个没有随机访问支持的长列表,第一种方法会慢得多.例如,在LinkedList上调用get(1000000)将必须从列表的开头循环1000000个元素以到达1000000th元素.第二种和第三种方法应该是等同的.

如果它在您的情况下是相关的,那么提供对其元素的恒定时间随机访问的List实现应该重写java.util.RandomAccess标记接口.至少标准API中的List实现可以.


fin*_*nnw 5

方法1不太可能是最好的方法,因为它没有其他方面的可读性或性能优势.

如果要跟踪索引,请考虑使用a ListIterator而不是a Iterator,例如

ListIterator<T> it = list.listIterator();
while (it.hasNext()) {
    T t = it.next;
    int index = it.previousIndex();
    System.out.printf("%d => %s%n", index, t);
}
Run Code Online (Sandbox Code Playgroud)

正如其他人所指出的,方法3等同于方法2,除非您想在迭代期间从列表中删除元素.

如果分析显示列表的get(int)方法或迭代器next()或者hasNext()是性能瓶颈(非常罕见但可能发生),请考虑用数组替换列表(您仍然可以使用for-each语法,类似于方法2).