【问题标题】:Java for each vs regular for -- are they equivalent?Java for each 与常规 for - 它们是否等效?
【发布时间】:2011-01-26 05:35:27
【问题描述】:

这两个结构是否等价?

char[] arr = new char[5];
for (char x : arr) {
    // code goes here
}

相比:

char[] arr = new char[5];
for (int i = 0; i < arr.length; i++) {
    char x = arr[i];
    // code goes here
}

也就是说,如果我将完全相同的代码放在两个循环的主体中(并且它们编译),它们的行为会完全相同吗???


完全免责声明:这是受到另一个问题 (Java: are these 2 codes the same) 的启发。我的答案原来不是答案,但我觉得 Java for-each 的确切语义有一些细微差别需要指出。

【问题讨论】:

    标签: java loops foreach syntactic-sugar


    【解决方案1】:

    虽然这两种结构通常可以互换,但它们不是 100% 等效的!!!

    可以通过定义// code goes here 来构造证明,这会导致两个构造的行为不同。一个这样的循环体是:

    arr = null;
    

    因此,我们现在比较:

    char[] arr = new char[5];
    for (char x : arr) {
        arr = null;
    }
    

    与:

    char[] arr = new char[5];
    for (int i = 0; i < arr.length; i++) {
        char x = arr[i];
        arr = null;
    }
    

    两个代码都可以编译,但是如果你运行它们,你会发现第一个循环正常终止,而第二个循环会抛出一个NullPointerException

    这意味着 它们不是 100% 等效的!在某些情况下,这两种构造的行为会有所不同!

    这种情况可能很少见,但调试时不应忘记这一事实,否则您可能会错过一些非常细微的错误。


    作为附录,请注意有时 for-each 结构甚至不是一个选项,例如如果你需要索引。这里的关键教训是,即使它是一个选项,你也需要确保它实际上是一个等效的替代品,因为它并不总是得到保证

    同样,如果您从 for-each 循环开始,后来意识到您需要切换到索引 for 循环,请确保您保留语义,因为它不能保证

    特别是,_注意对正在迭代的数组/集合的引用的任何修改_(修改内容可能/可能不会触发ConcurrentModificationException,但是这是一个不同的问题)。

    当您使用使用自定义迭代器的集合时,保证语义保留也困难得多,但正如本示例所示,即使涉及简单数组,这两种构造也是不同的。

    【讨论】:

    • 我会立即愉快地错误地说“是的,它们完全等价,因为第二种形式只是第一种形式的语法糖。”很好地指出了这种微妙的区别!
    • 感谢您抽出宝贵时间阅读本文。我希望其他人也能这样做,而不是一时兴起投反对票。
    • 非常好的答案,多基因润滑剂 (+1)。像卡尔一样,我没有意识到存在这种微妙的差异。谢谢你指出来。您可能还想在您的答案中添加这样一个事实,即第二个可以让您访问在某些情况下需要的索引。
    • 我对答案做了一些改进。随意提出任何其他需要解决的问题。
    【解决方案2】:

    第一个更具可读性,因此更可取,如果您无论如何都需要索引,那么请使用第二个。

    【讨论】:

      【解决方案3】:

      它们几乎相同。但在少数情况下它们不是。最好是final

      final char[] arr = new char[5];   // Now arr cannot be set null as shown 
                                        // in above answer.
      

      即使这样,您也可以在第二个循环中执行i--。如果你不做这些不太可能的事情,它们大多是等效的。

      【讨论】:

      • i-- 上为真,但该代码无论如何都不能移植到 for-each,因为它会引发编译时错误。不过,在final 上 +1。只要适用,总是一件好事。
      【解决方案4】:

      请注意,“polygenelubricants”给出的答案暗示了最重要的区别,但没有明确说明:for-each 遍历数组,但您不能使用循环给出的实例修改任何元素你(在这种情况下,变量“char x”)。经典循环允许您使用索引并更改数组的元素。

      编辑:快速更正。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-07-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-03-14
        • 1970-01-01
        • 2014-09-08
        相关资源
        最近更新 更多