19

这两个结构是等价的吗?

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:这两个代码是否相同)。我的答案原来不是答案,但我觉得 Java for-each 的确切语义有一些需要指出的细微差别。

4

4 回答 4

31

虽然这两种结构通常可以互换,但它们不是 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,但这是一个不同的问题)。

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

于 2010-03-11T05:14:00.653 回答
2

它们几乎是等价的。但在少数情况下它们不是。最好能做到final

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

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

于 2010-03-11T05:42:27.683 回答
1

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

于 2010-03-11T05:19:01.757 回答
1

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

编辑:快速更正。

于 2017-04-17T14:10:17.233 回答