这两个结构是否相同?
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的确切语义有一些细微差别需要指出。
答案 0 :(得分: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
,但这是一个不同的问题。)
当您使用使用自定义迭代器的集合时,保证语义保留也会变得更加困难,但正如此示例所示,即使涉及简单数组,这两种结构也是不同的。
答案 1 :(得分:2)
他们非常相同。但是很少有情况下没有。最好是final
。
final char[] arr = new char[5]; // Now arr cannot be set null as shown
// in above answer.
即使这样,您也可以在第二个循环中执行i--
。如果你不做一些不太可能的事情,那么它们大多是等价的。
答案 2 :(得分:1)
第一个更具可读性,因此更可取的是,如果你还需要索引,那么第二个就是丰满。
答案 3 :(得分:1)
请注意,“polygenelubricants”给出的答案暗示了最重要的区别,但没有明确说明:for-each遍历数组,但是你不能使用循环给出的实例修改任何元素你(在这种情况下,变量“char x”)。经典循环允许您使用索引并更改数组的元素。
编辑:快速更正。