如果有的话,下面两个循环之间的性能差异是什么?
for (Object o: objectArrayList) {
o.DoSomething();
}
and
for (int i=0; i<objectArrayList.size(); i++) {
objectArrayList.get(i).DoSomething();
}
如果有的话,下面两个循环之间的性能差异是什么?
for (Object o: objectArrayList) {
o.DoSomething();
}
and
for (int i=0; i<objectArrayList.size(); i++) {
objectArrayList.get(i).DoSomething();
}
当前回答
即使使用像ArrayList或Vector这样的东西,其中“get”是一个简单的数组查找,第二个循环仍然有第一个循环没有的额外开销。我预计它会比第一次慢一点。
其他回答
通过变量名称objectArrayList,我假设它是java.util.ArrayList的一个实例。在这种情况下,性能差异是不明显的。
另一方面,如果它是java.util的实例。LinkedList,第二种方法会慢得多,因为list# get(int)是一个O(n)操作。
因此,第一种方法总是首选的,除非循环中的逻辑需要索引。
for-each循环通常是首选的。如果您使用的List实现不支持随机访问,那么“get”方法可能会慢一些。例如,如果使用LinkedList,则会产生遍历代价,而For -each方法使用迭代器跟踪其在列表中的位置。关于for-each循环的细微差别的更多信息。
我想文章现在在这里:新的位置
这里显示的链接已经失效。
以下是Android开发团队对两者差异的简要分析:
https://www.youtube.com/watch?v=MZOf3pOAM6A
The result is that there is a difference, and in very restrained environments with very large lists it could be a noticeable difference. In their testing, the for each loop took twice as long. However, their testing was over an arraylist of 400,000 integers. The actual difference per element in the array was 6 microseconds. I haven't tested and they didn't say, but I would expect the difference to be slightly larger using objects rather than primitives, but even still unless you are building library code where you have no idea the scale of what you will be asked to iterate over, I think the difference is not worth stressing about.
在我看来,其他答案都是基于错误的基准测试,没有考虑Hotspot的编译和优化过程。
简短的回答
尽可能使用增强循环,因为大多数时候它是最快的。 如果不能,如果可能的话,将整个数组拉到一个局部变量中:
int localArray = this.array;
for (int i = 0; i < localArray.length; i++) {
methodCall(localArray[i]);
}
长回答
现在,通常没有区别,因为Hotspot非常擅长优化和消除java需要做的检查。
但有时一些优化就是无法完成,通常是因为在循环中有一个虚拟调用,不能内联。 在这种情况下,有些循环确实比其他循环快。
Java需要做的一些事情:
重新加载。数组——因为它可以被改变(通过调用或另一个线程) 检查i是否在数组的边界内,如果不是抛出IndexOutOfBoundsException 检查被访问对象引用是否为空,如果是则抛出NullPointerException异常
考虑一下这个c风格的循环:
for (int i = 0; i < this.array.length; i++) { //now java knows i < this.array.length
methodCall(this.array[i]);// no need to check
}
通过计算循环条件i < this.array。长度,Java知道I必须在边界内(I只在调用后更改),所以不需要在下一行中再次检查它。 但在这种情况下,java需要重新加载this.array.length。
你可能会想通过将this.array.length值拉到局部变量内部来“优化”循环:
int len = this.array.length;//len is now a local variable
for (int i = 0; i < len; i++) { //no reload needed
methodCall(this.array[i]); //now java will do the check
}
现在java不需要每次都重新加载,因为局部变量可以被methodCall和/或另一个线程改变。局部变量只能在方法本身内部更改,java现在可以证明变量len不能更改。
但是现在循环条件i < this.array.length变为i < len,之前的优化失败,java需要检查i in是否在this.array的边界内。
一个更好的优化方法是将整个数组拉入一个局部变量:
ArrayType[] localArray = this.array;
for (int i = 0; i < localArray.length; i++) {
methodCall(localArray[i]);
}
现在java不需要重新加载数组,“i in bounds”检查也被取消了。
那强化循环呢? 好吧,通常编译器会把增强的循环重写成类似上次显示的循环的东西,如果不是更好的话。
Foreach使你的代码的意图更清晰,这通常比非常小的速度改进更受欢迎——如果有的话。
每当我看到一个索引循环,我必须解析它一段时间,以确保它做什么,我认为它做,例如,它是否从零开始,它是否包括或排除终点等?
我的大部分时间似乎都花在了阅读代码(我写的或别人写的)上,而清晰度几乎总是比性能更重要。现在很容易忽略Hotspot的性能,因为Hotspot做得非常出色。