如果有的话,以下两个循环之间的性能差异是什么?
for (Object o: objectArrayList) { o.DoSomething(); }
和
for (int i=0; i
Vijay Dev.. 202
来自Joshua Bloch的Effective Java中的第46项:
在1.5版中引入的for-each循环通过完全隐藏迭代器或索引变量来消除混乱和错误的机会.由此产生的习语同样适用于集合和数组:
// The preferred idiom for iterating over collections and arrays for (Element e : elements) { doSomething(e); }当您看到冒号(:)时,将其读作"in".因此,上面的循环读作"对于元素中的每个元素e".请注意,使用for-each循环没有性能损失,即使对于数组也是如此.实际上,在某些情况下,它可能比普通的for循环提供轻微的性能优势,因为它只计算一次数组索引的限制.虽然您可以手动执行此操作(第45项),但程序员并不总是这样做.
分配迭代器会有性能损失.我在Android动态壁纸中有一些高度并行的代码.我看到垃圾收集器发疯了.这是因为for-each循环在许多不同的(短期)线程中分配临时迭代器,导致垃圾收集器做了很多工作.切换到基于常规索引的循环可以解决问题. (68认同)
Worth mentioning that in a for-each loop there is no way to access an index counter (since it doesn't exist) (47认同)
@ gsingh2011 Android文档(https://developer.android.com/training/articles/perf-tips.html#Loops)提到,仅在使用foreach而不是其他集合时,性能会受到影响.我很好奇这是不是你的情况. (2认同)
P Arrayah.. 29
所有这些循环完全相同,我只想在投入我的两分钱之前展示这些.
首先,循环遍历List的经典方法:
for(int i=0;i第二,首选的方式,因为它不容易出错(你做了多少次"oops,在循环中的这些循环中混合变量i和j"的事情?).
for(String s : strings) { /* do something using s */ }三,微优化for循环:
int size = strings.size(); for(int i=0;++i<=size;) { /* do something using strings.get(i) */ }现在实际的两分钱:至少当我测试这些时,第三个是最快的,当计算毫秒数时每个类型的循环需要多长时间,其中一个简单的操作重复几百万次 - 这是使用Java 5在Windows上使用jre1.6u10,以防有人感兴趣.
虽然它至少看起来是第三个是最快的,但你真的应该问自己,你是否想要冒险在你的循环代码中实现这个窥孔优化的风险,因为从我所看到的,实际的循环不是'通常是任何真实程序中最耗时的部分(或者我只是在错误的领域工作,谁知道).而且就像我在Java for-each循环(有些人将其称为Iterator循环,其他人称为for-in循环)的借口中提到的那样,在使用它时,你不太可能遇到那个特定的愚蠢错误.在讨论如何甚至比其他的更快之前,请记住javac根本不优化字节码(好吧,几乎无论如何),它只是编译它.
如果您正在进行微优化和/或您的软件使用大量递归循环等,那么您可能会对第三个循环类型感兴趣.只需记住在更改for循环之前和之后都要对您的软件进行基准测试,这对于这个奇怪的,微优化的循环.
1> Vijay Dev..:来自Joshua Bloch的Effective Java中的第46项:
在1.5版中引入的for-each循环通过完全隐藏迭代器或索引变量来消除混乱和错误的机会.由此产生的习语同样适用于集合和数组:
// The preferred idiom for iterating over collections and arrays for (Element e : elements) { doSomething(e); }当您看到冒号(:)时,将其读作"in".因此,上面的循环读作"对于元素中的每个元素e".请注意,使用for-each循环没有性能损失,即使对于数组也是如此.实际上,在某些情况下,它可能比普通的for循环提供轻微的性能优势,因为它只计算一次数组索引的限制.虽然您可以手动执行此操作(第45项),但程序员并不总是这样做.
分配迭代器会有性能损失.我在Android动态壁纸中有一些高度并行的代码.我看到垃圾收集器发疯了.这是因为for-each循环在许多不同的(短期)线程中分配临时迭代器,导致垃圾收集器做了很多工作.切换到基于常规索引的循环可以解决问题.
Worth mentioning that in a for-each loop there is no way to access an index counter (since it doesn't exist)
@ gsingh2011 Android文档(https://developer.android.com/training/articles/perf-tips.html#Loops)提到,仅在使用foreach而不是其他集合时,性能会受到影响.我很好奇这是不是你的情况.
2> P Arrayah..:所有这些循环完全相同,我只想在投入我的两分钱之前展示这些.
首先,循环遍历List的经典方法:
for(int i=0;i第二,首选的方式,因为它不容易出错(你做了多少次"oops,在循环中的这些循环中混合变量i和j"的事情?).
for(String s : strings) { /* do something using s */ }三,微优化for循环:
int size = strings.size(); for(int i=0;++i<=size;) { /* do something using strings.get(i) */ }现在实际的两分钱:至少当我测试这些时,第三个是最快的,当计算毫秒数时每个类型的循环需要多长时间,其中一个简单的操作重复几百万次 - 这是使用Java 5在Windows上使用jre1.6u10,以防有人感兴趣.
虽然它至少看起来是第三个是最快的,但你真的应该问自己,你是否想要冒险在你的循环代码中实现这个窥孔优化的风险,因为从我所看到的,实际的循环不是'通常是任何真实程序中最耗时的部分(或者我只是在错误的领域工作,谁知道).而且就像我在Java for-each循环(有些人将其称为Iterator循环,其他人称为for-in循环)的借口中提到的那样,在使用它时,你不太可能遇到那个特定的愚蠢错误.在讨论如何甚至比其他的更快之前,请记住javac根本不优化字节码(好吧,几乎无论如何),它只是编译它.
如果您正在进行微优化和/或您的软件使用大量递归循环等,那么您可能会对第三个循环类型感兴趣.只需记住在更改for循环之前和之后都要对您的软件进行基准测试,这对于这个奇怪的,微优化的循环.
编写微优化循环的更好方法是(int i = 0,size = strings.size(); ++ i <= size;){}这是优选的,因为它最小化了大小的范围
请注意,++ i <= size的for循环是"基于1的",例如循环内的get-method将被调用值1,2,3等.
3> Zach Scriven..:通常应首选for-each循环.如果您使用的List实现不支持随机访问,则"get"方法可能会更慢.例如,如果使用LinkedList,则会产生遍历开销,而for-each方法使用迭代器来跟踪其在列表中的位置.有关for-each循环细微差别的更多信息.
我认为这篇文章现在在这里: 新的位置
这里显示的链接已经死了.
4> Sarmun..:那么,性能影响大多是微不足道的,但不是零.如果你看一下
RandomAccess
界面的JavaDoc :根据经验,对于类的典型实例,如果此循环,List实现应该实现此接口:
for (int i=0, n=list.size(); i < n; i++) list.get(i);运行速度比这个循环快:
for (Iterator i=list.iterator(); i.hasNext();) i.next();并且for-each循环使用带有迭代器的版本,因此
ArrayList
例如,for-each循环不是最快的.
5> Gary Gregory..:不幸的是,似乎存在差异.
如果你看两种循环的生成字节代码,它们是不同的.
以下是Log4j源代码的示例.
在/log4j-api/src/main/java/org/apache/logging/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随着每个:
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和8.
对于那些试图阅读反汇编的人来说,最终结果是循环内生成的代码是相同的,但for-each设置似乎创建了一个额外的临时变量,其中包含对第二个参数的引用.如果注册了额外的隐藏变量,但参数本身不是在代码生成期间,则for-each会更快; 如果参数在for(;;)示例中注册,则执行时间将相同.要有基准吗?