简体   繁体   English

性能:通过Java中的List进行迭代

[英]Performance: Iterating through a List in Java

Is it slower to iterate through a list in Java like this: 在Java中迭代遍历列表是否更慢:

for (int i=0;i<list.size();i++) {
    .. list.get(i)
}

as opposed to: 而不是:

for (Object o: list) {
    ... o
}

I assume you ask out of pure curiosity and won't cite Knuth (somebody probably will). 我假设你出于纯粹的好奇心而不会引用Knuth(有人可能会这样)。

I believe that once your code gets compiled, it doesn't make a difference. 我相信一旦你的代码被编译,它就没有什么区别。 It does make a difference before (example 2 is a lot more readable and concise), so go for number 2 and do not care about the rest. 之前 确实有所不同(示例2更具可读性和简洁性),所以请选择2号而不关心其余部分。

Just my 2 cents 只需2美分

EDIT 编辑

Note your code in snippet number 1 calculates list.size() every time the loop runs, that could make it even slower than number 2 请注意,每次循环运行时,您在第1个代码段中的代码计算list.size() ,这可能会比第2个更慢

YET ANOTHER EDIT 再来一次编辑

Something I had to double check, Joshua Bloch recommends using for each loops (see item 46 of Effective Java ). 我不得不仔细检查,Joshua Bloch建议for each循环使用(参见Effective Java 46项)。 I believe that ends all kinds of discussions. 我认为这结束了各种讨论。 Thanks Josh! 谢谢Josh! :) :)

There shouldn't be any noticeable differences in performance for normal lists. 普通列表的性能不应有任何明显差异。

For linked lists, the iterator will be substantially faster, especially for large lists. 对于链表,迭代器将大大加快,特别是对于大型列表。

Created a microbenchmark for the question and was surprised to see for-each runing 2x-3x faster than an indexed loop. 为这个问题创建了一个微基准测试 ,并且惊讶地发现 - 每次运行比索引循环快2x-3x。 The only explanation I have is that for-each version might not require range checks which are made by ArrayList.get(int index). 我唯一的解释是for-each版本可能不需要由ArrayList.get(int index)进行范围检查。

For very small lists (10 elements) the result was about the same. 对于非常小的列表(10个元素),结果大致相同。 For 100 elements for-each is 1.5x faster, for 10000-100000 elements it is faster 2x-3x times. 对于100个元素 - 每个元素快1.5倍,对于10000-100000个元素,速度快2倍-3倍。

The tests are run on a random dataset and checksums are being verified at the end, so JIT chearing is very unlikely to take place in these. 测试在随机数据集上运行,并且最后验证校验和,因此在这些中不太可能发生JIT。

According to benchmark tests in While loop, For loop and Iterator Performance Test – Java and the JavaRanch question " is using ArrayList.iterator() is faster than looping ArrayList in for loop? " the iterator is actually a bit slower. 根据While循环中的基准测试,For循环和Iterator性能测试 - Java和JavaRanch问题“ 正在使用ArrayList.iterator()比在循环中循环ArrayList更快吗? ”迭代器实际上有点慢。

I'd still favor readability unless I'd benchmarked my entire application and found that loop to be my bottleneck, though. 我仍然喜欢可读性,除非我对我的整个应用程序进行基准测试,并发现循环是我的瓶颈。

No. It is faster (or should be faster) when the list also implements: RandomAccess (as ArrayList does and LinkedList doesn't). 不。当列表也实现时,速度更快(或者应该更快): RandomAccess (如ArrayList所做,而LinkedList则不然)。

However, you should always use the latter : 但是,你应该总是使用后者:

 for( Object o: list ) {
 }

and only switch to the former if your have substantial evidence that you're having a performance issue using it (for instance, you profile your application and as result you see as a point for improvement this section of code). 如果您有充分的证据表明您使用它有性能问题(例如,您对应用程序进行了分析,结果您认为这部分代码的改进点),那么只能切换到前者。

By not doing so, you risk not being able to switch your implementation in a later refactoring if your application requires it (because you would be tied to the for( int i = 0 ; i < list.size(); i++ ) idiom). 如果你的应用程序需要它,你就有可能无法在以后的重构中切换你的实现(因为你将被绑定到for( int i = 0 ; i < list.size(); i++ ) idiom) 。

THERE CAN BE A DIFFERENCE. 可能有所不同。

If a List implementation also implements java.util.RandomAccess (like ArrayList does), then it is just about faster to use its get() method over an interator. 如果List实现也实现了java.util.RandomAccess(就像ArrayList那样),那么在Interator上使用它的get()方法要快得多。

If it does not implement java.util.RandomAccess (for example, LinkedList does not), then it is substantially faster to use an iterator. 如果它没有实现java.util.RandomAccess(例如,LinkedList没有),那么使用迭代器要快得多。

However, this only matter if you are using lists containing thousands of (possibly scattered) objects or that are constantly traversed (as if performing copious amounts of math on List objects representing numerical vectors.) 但是,这仅限于使用包含数千个(可能是分散的)对象的列表或者经常遍历的列表(就像在表示数字向量的List对象上执行大量数学运算一样。)

Checking the size every iteration does add i operations, but it's not a big impact on performance. 检查每次迭代的大小确实会增加i操作,但这对性能没有太大影响。

By that, I mean there is a minor difference between 通过这个,我的意思是两者之间存在细微差别

int lsize = myList.size();
for(int i=0; i < lsize; i++)
{
   Object o = myList.get(i);
}

Versus: 与:

for(int i=0; i < myList.size(); i++)
{
   Object o = myList.get(i);
}

But it essentially doesn't matter. 但它基本上没关系。 Use the second one from your question for readability, among other reasons. 除了其他原因之外,请使用问题中的第二个来提高可读性。

It is recognized that the distinction between random and sequential * access is often fuzzy. 人们认识到随机和顺序*访问之间的区别通常是模糊的。 For example, some List implementations * provide asymptotically linear access times if they get huge, but constant * access times in practice. 例如,某些List实现*提供渐近线性访问时间,如果它们变大,但实际上是恒定的访问时间。 Such a List implementation * should generally implement this interface. 这样的List实现*通常应该实现这个接口。 As a rule of thumb, a * List implementation should implement this interface if, * for typical instances of the class, this loop: * 根据经验,* List实现应实现此接口if,*对于类的典型实例,此循环:*

*     for (int i=0, n=list.size(); i < n; i++)
 *         list.get(i);
 *
* runs faster than this loop: * *比这个循环运行得更快:*
\n * for (Iterator i=list.iterator(); i.hasNext(); ) * for(Iterator i = list.iterator(); i.hasNext();)\n * i.next(); * i.next();\n * * 
* *

I didn't look myself into the code of get() of all List implementations so maybe what I will write is some FUD. 我没有看到所有List实现的get()代码,所以也许我会写的是一些FUD。 What I have learned is that using the get(i) in for loop will result in an iteration over the whole list over and over again each loop run. 我学到的是在for循环中使用get(i)将导致在每个循环运行中反复遍历整个列表。 Using an Iterator (like enhanced for loop does) will just move the iterator the the next list element without iterating the whole list again. 使用迭代器(如增强for for循环)只会将迭代器移动到下一个列表元素,而无需再次迭代整个列表。

My conclusion is that using iterators or the enhanced for loop should be more performant since using get() will result in complexity O(n^2) instead of O(n) . 我的结论是使用迭代器或增强的for循环应该更高效,因为使用get()将导致复杂度O(n^2)而不是O(n)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM