如果有的话,下面两个循环之间的性能差异是什么?
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();
}
当前回答
1. for(Object o: objectArrayList){
o.DoSomthing();
}
and
2. for(int i=0; i<objectArrayList.size(); i++){
objectArrayList.get(i).DoSomthing();
}
两者都做同样的事情,但为了方便和安全的编程使用,在第二种使用方式中有容易出错的可能性。
其他回答
对性能的影响基本上是微不足道的,但也不是零。如果你看看JavaDoc的RandomAccess接口:
根据经验,一个列表 实现应该实现这个 的典型实例 这个类,这个循环: For (int i=0, n=list.size();I < n;我+ +) list.get(我); 运行速度比这个循环快: for(迭代器i=list.iterator();i.hasNext (); i.next ();
for-each循环使用version with iterator,以ArrayList为例,for-each循环不是最快的。
在我看来,其他答案都是基于错误的基准测试,没有考虑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”检查也被取消了。
那强化循环呢? 好吧,通常编译器会把增强的循环重写成类似上次显示的循环的东西,如果不是更好的话。
使用迭代器总是比使用索引更好。这是因为iterator最有可能针对List实现进行了优化,而索引(调用get)可能没有。例如,LinkedList是一个List,但是通过它的元素建立索引将比使用迭代器迭代慢。
for-each循环通常是首选的。如果您使用的List实现不支持随机访问,那么“get”方法可能会慢一些。例如,如果使用LinkedList,则会产生遍历代价,而For -each方法使用迭代器跟踪其在列表中的位置。关于for-each循环的细微差别的更多信息。
我想文章现在在这里:新的位置
这里显示的链接已经失效。
Foreach使你的代码的意图更清晰,这通常比非常小的速度改进更受欢迎——如果有的话。
每当我看到一个索引循环,我必须解析它一段时间,以确保它做什么,我认为它做,例如,它是否从零开始,它是否包括或排除终点等?
我的大部分时间似乎都花在了阅读代码(我写的或别人写的)上,而清晰度几乎总是比性能更重要。现在很容易忽略Hotspot的性能,因为Hotspot做得非常出色。