每个vs常规的Java – 它们是等价的吗?

这两个结构是否相同?

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

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

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

 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警告对迭代的数组/集合的引用的任何修改_( 对内容的修改可能/可能不会触发ConcurrentModificationException ,但这是一个不同的问题)。

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

它们非常相同 。 但是很少有这样的情况。 最好让它成为final

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

即便如此,你可以在第二个循环中做i-- 如果你不做一些这些不太可能的事情,他们大多是等同的。

第一个更具可读性,因此更可取,如果你还需要索引,那么第二个就是丰满。

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

编辑:快速更正。