如果有的话,下面两个循环之间的性能差异是什么?
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();
}
当前回答
奇怪的是,没有人提到显而易见的——foreach分配内存(以迭代器的形式),而普通的for循环不分配任何内存。对于Android游戏来说,这是个问题,因为这意味着垃圾收集器将周期性地运行。在游戏中,你不希望垃圾回收器运行……永远。所以不要在你的绘制(或渲染)方法中使用foreach循环。
其他回答
Accepted answer回答了这个问题,除了ArrayList…
因为大多数开发人员都依赖于ArrayList(至少我是这么认为的)
所以我有义务在这里加上正确答案。
直接从开发人员文档:-
增强的for循环(有时也称为“for-each”循环)可用于实现Iterable接口的集合和数组。对于集合,会分配一个迭代器来对hasNext()和next()进行接口调用。使用ArrayList,手工编写的计数循环大约快3倍(有或没有JIT),但对于其他集合,增强的for循环语法将完全等同于显式迭代器的使用。
有几种迭代数组的方法:
static class Foo {
int mSplat;
}
Foo[] mArray = ...
public void zero() {
int sum = 0;
for (int i = 0; i < mArray.length; ++i) {
sum += mArray[i].mSplat;
}
}
public void one() {
int sum = 0;
Foo[] localArray = mArray;
int len = localArray.length;
for (int i = 0; i < len; ++i) {
sum += localArray[i].mSplat;
}
}
public void two() {
int sum = 0;
for (Foo a : mArray) {
sum += a.mSplat;
}
}
zero()是最慢的,因为JIT还不能优化掉每次循环迭代获取数组长度的成本。
一个()比较快。它将所有内容提取到局部变量中,避免了查找。只有数组长度能带来性能上的好处。
two()对于没有JIT的设备来说是最快的,对于有JIT的设备来说与one()是没有区别的。它使用Java编程语言1.5版中引入的增强for循环语法。
因此,默认情况下您应该使用增强的for循环,但是可以考虑使用手写的计数循环来进行性能关键的ArrayList迭代。
摘自Joshua Bloch的《Effective Java》第46条:
The for-each loop, introduced in release 1.5, gets rid of the clutter and the opportunity for error by hiding the iterator or index variable completely. The resulting idiom applies equally to collections and arrays: // The preferred idiom for iterating over collections and arrays for (Element e : elements) { doSomething(e); } When you see the colon (:), read it as “in.” Thus, the loop above reads as “for each element e in elements.” Note that there is no performance penalty for using the for-each loop, even for arrays. In fact, it may offer a slight performance advantage over an ordinary for loop in some circumstances, as it computes the limit of the array index only once. While you can do this by hand (Item 45), programmers don’t always do so.
对性能的影响基本上是微不足道的,但也不是零。如果你看看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循环不是最快的。
以下是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.
不幸的是,两者之间似乎存在差异。
如果查看这两种循环生成的字节代码,就会发现它们是不同的。
下面是来自Log4j源代码的一个示例。
在/log4j-api/src/main/java/org/apache/ loggging/ log4j/ markermanager .java中,我们有一个名为Log4jMarker的静态内部类,它定义了:
/*
* Called from add while synchronized.
*/
private static boolean contains(final Marker parent, final Marker... localParents) {
//noinspection ForLoopReplaceableByForEach
for (final Marker marker : localParents) {
if (marker == parent) {
return true;
}
}
return false;
}
使用标准循环:
private static boolean contains(org.apache.logging.log4j.Marker, org.apache.logging.log4j.Marker...);
Code:
0: iconst_0
1: istore_2
2: aload_1
3: arraylength
4: istore_3
5: iload_2
6: iload_3
7: if_icmpge 29
10: aload_1
11: iload_2
12: aaload
13: astore 4
15: aload 4
17: aload_0
18: if_acmpne 23
21: iconst_1
22: ireturn
23: iinc 2, 1
26: goto 5
29: iconst_0
30: ireturn
for - each:
private static boolean contains(org.apache.logging.log4j.Marker, org.apache.logging.log4j.Marker...);
Code:
0: aload_1
1: astore_2
2: aload_2
3: arraylength
4: istore_3
5: iconst_0
6: istore 4
8: iload 4
10: iload_3
11: if_icmpge 34
14: aload_2
15: iload 4
17: aaload
18: astore 5
20: aload 5
22: aload_0
23: if_acmpne 28
26: iconst_1
27: ireturn
28: iinc 4, 1
31: goto 8
34: iconst_0
35: ireturn
那个神谕是怎么回事?
我在Windows 7上用Java 7和Java 8尝试过。