for循环和for循环之间是否存在性能差异?

efl*_*les 176 java performance for-loop

如果有的话,以下两个循环之间的性能差异是什么?

for (Object o: objectArrayList) {
    o.DoSomething();
}
Run Code Online (Sandbox Code Playgroud)

for (int i=0; i<objectArrayList.size(); i++) {
    objectArrayList.get(i).DoSomething();
}
Run Code Online (Sandbox Code Playgroud)

Vij*_*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);
}
Run Code Online (Sandbox Code Playgroud)

当您看到冒号(:)时,将其读作"in".因此,上面的循环读作"对于元素中的每个元素e".请注意,使用for-each循环没有性能损失,即使对于数组也是如此.实际上,在某些情况下,它可能比普通的for循环提供轻微的性能优势,因为它只计算一次数组索引的限制.虽然您可以手动执行此操作(第45项),但程序员并不总是这样做.

  • 分配迭代器会有性能损失.我在Android动态壁纸中有一些高度并行的代码.我看到垃圾收集器发疯了.这是因为for-each循环在许多不同的(短期)线程中分配临时迭代器,导致垃圾收集器做了很多工作.切换到基于常规索引的循环可以解决问题. (68认同)
  • 值得一提的是,在 for-each 循环中,无法访问索引计数器(因为它不存在) (47认同)
  • @ gsingh2011 Android文档(https://developer.android.com/training/articles/perf-tips.html#Loops)提到,仅在使用foreach而不是其他集合时,性能会受到影响.我很好奇这是不是你的情况. (2认同)

P A*_*yah 29

所有这些循环完全相同,我只想在投入我的两分钱之前展示这些.

首先,循环遍历List的经典方法:

for(int i=0;i<strings.size();i++) { /* do something using strings.get(i) */ }
Run Code Online (Sandbox Code Playgroud)

第二,首选的方式,因为它不容易出错(你做了多少次"oops,在循环中的这些循环中混合变量i和j"的事情?).

for(String s : strings) { /* do something using s */ }
Run Code Online (Sandbox Code Playgroud)

三,微优化for循环:

int size = strings.size();
for(int i=0;++i<=size;) { /* do something using strings.get(i) */ }
Run Code Online (Sandbox Code Playgroud)

现在实际的两分钱:至少当我测试这些时,第三个是最快的,当计算毫秒数时每个类型的循环需要多长时间,其中一个简单的操作重复几百万次 - 这是使用Java 5在Windows上使用jre1.6u10,以防有人感兴趣.

虽然它至少看起来是第三个是最快的,但你真的应该问自己,你是否想要冒险在你的循环代码中实现这个窥孔优化的风险,因为从我所看到的,实际的循环不是'通常是任何真实程序中最耗时的部分(或者我只是在错误的领域工作,谁知道).而且就像我在Java for-each循环(有些人将其称为Iterator循环,其他人称为for-in循环)的借口中提到的那样,在使用它时,你不太可能遇到那个特定的愚蠢错误.在讨论如何甚至比其他的更快之前,请记住javac根本不优化字节码(好吧,几乎无论如何),它只是编译它.

如果您正在进行微优化和/或您的软件使用大量递归循环等,那么您可能会对第三个循环类型感兴趣.只需记住在更改for循环之前和之后都要对您的软件进行基准测试,这对于这个奇怪的,微优化的循环.

  • 编写微优化循环的更好方法是(int i = 0,size = strings.size(); ++ i <= size;){}这是优选的,因为它最小化了大小的范围 (15认同)
  • 请注意,++ i <= size的for循环是"基于1的",例如循环内的get-method将被调用值1,2,3等. (5认同)

Zac*_*ena 13

通常应首选for-each循环.如果您使用的List实现不支持随机访问,则"get"方法可能会更慢.例如,如果使用LinkedList,则会产生遍历开销,而for-each方法使用迭代器来跟踪其在列表中的位置.有关for-each循环细微差别的更多信息.

我认为这篇文章现在在这里: 新的位置

这里显示的链接已经死了.


Sar*_*mun 11

那么,性能影响大多是微不足道的,但不是零.如果你看一下RandomAccess界面的JavaDoc :

根据经验,对于类的典型实例,如果此循环,List实现应该实现此接口:

for (int i=0, n=list.size(); i < n; i++)
    list.get(i);
Run Code Online (Sandbox Code Playgroud)

运行速度比这个循环快:

for (Iterator i=list.iterator(); i.hasNext();)
      i.next();
Run Code Online (Sandbox Code Playgroud)

并且for-each循环使用带有迭代器的版本,因此ArrayList例如,for-each循环不是最快的.


For*_*ner 5

foreach 使您的代码的意图更清晰,并且通常比非常小的速度改进更受欢迎 - 如果有的话。

每当我看到一个索引循环时,我都必须对其进行更长时间的解析,以确保它执行我认为的操作,例如它是否从零开始,是否包含或排除终点等?

我的大部分时间似乎都花在阅读代码(我写的或其他人写的)上,清晰几乎总是比性能更重要。现在很容易忽略性能,因为 Hotspot 做得如此出色。


Gar*_*ory 5

不幸的是,似乎存在差异.

如果你看两种循环的生成字节代码,它们是不同的.

以下是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;
    }
Run Code Online (Sandbox Code Playgroud)

使用标准循环:

  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
Run Code Online (Sandbox Code Playgroud)

随着每个:

  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
Run Code Online (Sandbox Code Playgroud)

甲骨文怎么了?

我在Windows 7上尝试使用Java 7和8.

  • 对于那些试图阅读反汇编的人来说,最终结果是循环内生成的代码是相同的,但for-each设置似乎创建了一个额外的临时变量,其中包含对第二个参数的引用.如果注册了额外的隐藏变量,但参数本身不是在代码生成期间,则for-each会更快; 如果参数在for(;;)示例中注册,则执行时间将相同.要有基准吗? (7认同)