虽然这两种结构通常可以互换,但它们不是 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
,但这是一个不同的问题)。
当您使用使用自定义迭代器的集合时,保证语义保留也困难得多,但正如此示例所示,即使涉及简单数组,这两种构造也是不同的。