当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《for循环和for循环之间是否存在性能差异?》经验,为你挑选了5个好方法。

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

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(;;)示例中注册,则执行时间将相同.要有基准吗?
推荐阅读
mylvfamily
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有