【问题标题】:Fastest way to iterate an Array in Java: loop variable vs enhanced for statement [duplicate]在 Java 中迭代数组的最快方法:循环变量与增强的 for 语句 [重复]
【发布时间】:2010-11-03 15:10:22
【问题描述】:

在 Java 中,以老式方式遍历数组是否更快,

for (int i = 0; i < a.length; i++)
    f(a[i]);

或者使用更简洁的形式,

for (Foo foo : a)
    f(foo);

对于一个ArrayList,答案是否一样?

当然,对于大量的应用程序代码,答案是它没有明显的区别,因此应该使用更简洁的形式以提高可读性。然而,我正在查看的上下文是重型技术计算,必须执行数十亿次的操作,因此即使是微小的速度差异最终也可能很重要。

【问题讨论】:

  • 这是asked before
  • 为什么不运行基准测试?比讨论准确得多。
  • 那不是数组。在内部的“问题前询问”中,将使用 foreach 循环创建一个迭代器,而这里它不会根据这里的答案。
  • 除非f() 非常短,否则您不太可能注意到差异。
  • 从技术上讲,这确实是一个不同的问题。数组和列表是有区别的。因此,技术实现是不同的,可能会导致不同的结果。 Jon Skeet 的回答对数组很好。

标签: java performance iteration


【解决方案1】:

如果您正在循环一个数组,那应该没关系 - 增强的 for 循环无论如何都会使用数组访问。

例如,考虑以下代码:

public static void main(String[] args)
{
    for (String x : args)
    {
        System.out.println(x);
    }
}

当使用javap -c Test 反编译时,我们得到(对于main 方法):

public static void main(java.lang.String[]);
  Code:
   0:   aload_0
   1:   astore_1
   2:   aload_1
   3:   arraylength
   4:   istore_2
   5:   iconst_0
   6:   istore_3
   7:   iload_3
   8:   iload_2
   9:   if_icmpge   31
   12:  aload_1
   13:  iload_3
   14:  aaload
   15:  astore  4
   17:  getstatic   #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   20:  aload   4
   22:  invokevirtual   #3; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   25:  iinc    3, 1
   28:  goto    7
   31:  return

现在将其更改为使用显式数组访问:

public static void main(String[] args)
{
    for (int i = 0; i < args.length; i++)
    {
        System.out.println(args[i]);
    }
}

这反编译为:

public static void main(java.lang.String[]);
  Code:
   0:   iconst_0
   1:   istore_1
   2:   iload_1
   3:   aload_0
   4:   arraylength
   5:   if_icmpge   23
   8:   getstatic   #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   11:  aload_0
   12:  iload_1
   13:  aaload
   14:  invokevirtual   #3; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   17:  iinc    1, 1
   20:  goto    2
   23:  return

在增强的 for 循环中有更多设置代码,但它们基本上在做同样的事情。不涉及迭代器。此外,我希望他们能够 JITted 到更相似的代码。

建议:如果您真的认为它可能会产生显着差异(只有在循环体绝对微不足道的情况下,它才会永远这样做),那么您应该用您的实际应用程序对其进行基准测试。这是唯一重要的情况。

【讨论】:

    【解决方案2】:

    这完全属于micro-optimization 的领域。真的没关系。从风格上讲,我总是更喜欢第二种,因为它更简洁,除非你需要循环计数器来做其他事情。这远比这种微优化更重要:可读性。

    话虽如此,对于 ArrayList 不会有太大区别,但 LinkedList 与第二个相比会更有效率。

    【讨论】:

    • 它不会对数组使用迭代器。
    • 第一种样式限制为 Integer.MAX 迭代次数,而第二种则没有。
    • 第二个也有这种限制,因为数组有最大 Integer.MAX_VALUE 个元素,而 ArrayList 由数组支持。
    【解决方案3】:

    测量它。所有性能问题的答案可能取决于 VM 版本、处理器、内存速度、缓存等。因此您必须针对您的特定平台进行衡量。

    我个人更喜欢第二种变体,因为意图更清楚。如果性能成为问题,我无论如何都可以稍后对其进行优化——如果该代码对整个应用程序的性能真的很重要。

    【讨论】:

      【解决方案4】:

      对于链表:

      for(ClassOfElement element : listOfElements) {
        System.out.println(element.getValue());
      }
      

      之前回答过:

      Is there a performance difference between a for loop and a for-each loop?

      【讨论】:

        【解决方案5】:

        在数组或 RandomAccess 集合上,您可以通过以下方式稍微提高速度:

        List<Object> list = new ArrayList<Object>();
        
        for (int i=0, d=list.size(); i<d; i++) {
            something(list.get(i));
        }
        

        但我一般不会担心。像这样的优化不会对您的代码产生超过 0.1% 的差异。尝试使用 -prof 调用 java 以查看您的代码实际花费的时间。

        【讨论】:

          【解决方案6】:

          更快的是使用fork-join框架的ParallelArray(如果你有足够大的数据集)。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2010-09-22
            • 2015-11-03
            • 2014-11-16
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-01-05
            • 2020-02-06
            相关资源
            最近更新 更多