每个与常规的Java相同 - 它们是等价的吗?

pol*_*nts 19 java foreach loops syntactic-sugar

这两个结构是否相同?

char[] arr = new char[5];
for (char x : arr) {
    // code goes here
}
Run Code Online (Sandbox Code Playgroud)

相比:

char[] arr = new char[5];
for (int i = 0; i < arr.length; i++) {
    char x = arr[i];
    // code goes here
}
Run Code Online (Sandbox Code Playgroud)

也就是说,如果我在两个循环的主体中放置完全相同的代码(并且它们编译),它们的行为是否完全相同?


完全免责声明:这是受另一个问题的启发(Java:这两个代码是相同的).我的答案结果不是答案,但我觉得Java for-each的确切语义有一些细微差别需要指出.

pol*_*nts 31

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

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

arr = null;
Run Code Online (Sandbox Code Playgroud)

因此,我们现在比较:

char[] arr = new char[5];
for (char x : arr) {
    arr = null;
}
Run Code Online (Sandbox Code Playgroud)

有:

char[] arr = new char[5];
for (int i = 0; i < arr.length; i++) {
    char x = arr[i];
    arr = null;
}
Run Code Online (Sandbox Code Playgroud)

两个代码都编译,但如果你运行它们,你会发现第一个循环正常终止,而第二个循环将抛出一个NullPointerException.

这意味着它们不是100%相同!在某些情况下,这两种结构的行为会有所不同!

这种情况可能很少见,但在调试时不应忘记这个事实,因为否则你可能会错过一些非常微妙的错误.


作为附录,请注意,有时for-each构造甚至不是一个选项,例如,如果您需要索引.这里的关键教训是即使它是一个选项,你需要确保它实际上是一个等价的替代品,因为它并不总是保证

类似地,如果你从for-each循环开始,后来意识到你需要切换到索引for循环,请确保你保留了语义,因为它不能保证.

特别是,_be警告对迭代的数组/集合的引用进行任何修改_(对内容的修改可能/可能不会触发ConcurrentModificationException,但这是一个不同的问题).

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

  • 我会立刻,轻率地,错误地说"是的,它们是完全等同的,因为第二种形式只是第一种形式的糖." 做得好,指出这种微妙的区别! (4认同)
  • 非常优秀的答案,polygenelubricants(+1).像卡尔一样,我没有意识到存在这种微妙的差异.谢谢你指出来.您可能还想在答案中添加一个事实,即第二个将允许您访问某些情况下所需的索引. (3认同)