Java 对于每个与常规的 - 它们是等效的吗?

2022-09-02 12:24:24

这两种结构是否等效?

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的确切语义有一些需要指出的细微差别。


答案 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 循环,请确保保留语义,因为它不能保证

特别是,_be对数组/集合引用的任何修改都iterated_(对内容的修改可能/可能不会触发,但这是一个不同的问题)。ConcurrentModificationException

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


答案 2

它们几乎是等效的。但很少有情况下它们不是。最好是让它。final

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

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


推荐