【问题标题】:Performance Difference For-Loop ForeachFor-Loop Foreach 的性能差异
【发布时间】:2016-09-09 08:44:29
【问题描述】:

我总是问自己“使用什么”应该使用 for 循环还是 foreach。 在我看来,两者都是“相同的”。我知道遍历列表等是一个更好的 foreach 但如果我们有以下情况怎么办:

for (String zipCode : zipCodes) {
    if (zipCode.equals(zip)) {
        return true;
    }
}

for (int i = 0; i < zipCodes.length; i++) {
    if (zipCodes[i].equals(zip)) {
        return true;
    }
}

什么会更好?还是在这种情况下真的没有区别?

【问题讨论】:

  • 哪个更具可读性?
  • 至于性能 - 自己试试
  • 另外,在这种特殊情况下,zipCodes.contains(zip) 是最好的。
  • 我不得不说它是一个数组,我确实可以将它转换为一个列表,但这将是低效的。在我看来,第一种情况更具可读性

标签: java


【解决方案1】:

首要任务 - for-each 只是 Iterator 的语法糖。阅读 JLS 的this section。所以,我将把这个问题作为一个简单的 FOR 循环与迭代器来解决。

现在,当您使用 Iterator 遍历集合时,您至少会使用两种方法 - next()hasNext(),以下是它们的 ArrayList 实现:

    public boolean hasNext() {
        return cursor != size;
    }

    @SuppressWarnings("unchecked")
    public E next() {
        checkForComodification();
        int i = cursor;
        if (i >= size)
            throw new NoSuchElementException();
        Object[] elementData = ArrayList.this.elementData;
        if (i >= elementData.length)
            throw new ConcurrentModificationException();
        cursor = i + 1;
        return (E) elementData[lastRet = I]; // hagrawal: this is what simple FOR loop does
    }

现在,我们都知道基本计算,如果在处理器上我必须只执行myArray[i] v/s 完整实现next() 方法,就会有性能差异。因此,性能必须有所不同。

很可能有些人可能会强烈反对这一点,引用性能基准和来自 Effective Java 的摘录,但我可以尝试解释的唯一其他方式是这甚至写在 Oracle 的官方文档中 - 请阅读以下内容RandomAccess 接口文档超过 here

非常明确地提到会有差异。因此,如果您能说服我官方文档中写的内容是错误的并且会被更改,我将准备接受简单 FOR 循环与 Iterator 或 for-each 之间没有性能差异的论点。

恕我直言,提出整个论点的正确方法是:

  1. 如果集合实现RandomAccess 接口,那么简单的FOR 循环将比Iterator 或for-each 执行(至少理论上)更好。 (这也是RandomAccess docs写的)
  2. 如果集合没有实现 RandomAccess 接口,那么 Iterator 或 for-each 将(肯定)比简单的 FOR 循环执行得更好。
  3. 但是,出于所有实际目的,for-each 通常是最佳选择。

【讨论】:

    【解决方案2】:

    如果zipCodes[i] 不是 O(1),那么你的第二种情况的性能会差很多。 (也就是说,我认为在 Java 中存在一个容器,其中[]不是 O(1))。换句话说,短形式 for 循环不能变慢。

    加上for 循环的缩写形式更清晰,这确实应该是首要考虑因素,除非速度很重要

    【讨论】:

    • 只是为了澄清那些不知道的人:O(1) 表示算法不依赖于元素的数量。这与O(n) 不同,在O(n) 中,添加的元素越多,算法所花费的时间就越长。见:stackoverflow.com/questions/697918/…
    【解决方案3】:

    如今,优化已不再那么重要,因为任何差异都不会引起注意,除非您需要处理大量数据。此外,如果您使用 Collection,则性能将取决于所选的实现。

    您真正应该考虑的是代码的质量。规则是您应该使用尽可能少的元素来尽可能清晰地呈现逻辑。第二种解决方案引入了一个新元素 i 索引,它实际上并不需要,只会使代码稍微复杂一些。仅当您确实需要知道每次迭代中的索引时才使用 fori 循环。

    因此,从代码质量的角度来看,您应该使用第一个解决方案 :-)

    【讨论】:

      【解决方案4】:

      请注意,使用 for-each 循环不会降低性能, 即使是数组。事实上,它可能会提供轻微的性能优势 在某些情况下通过普通的 for 循环,因为它计算 数组索引的限制只有一次。

      Joshua Bloch 在 Effective Java 中的第 46 条

      【讨论】:

      • computes the limit of the array index only once. 这已经过时了,所有计数的循环都会接受 JIT 优化,并且以数组大小为界的循环会按照例行程序删除索引检查。
      猜你喜欢
      • 2019-06-03
      • 2013-02-18
      • 2010-11-10
      • 1970-01-01
      • 1970-01-01
      • 2019-02-09
      • 2012-01-03
      • 2011-08-08
      • 1970-01-01
      相关资源
      最近更新 更多