Ответ 1
В семантике расширенного цикла for
Вот соответствующие выдержки из Спецификации Java Language Specification 3rd Edition, слегка отредактированные для ясности:
JLS 14.14.2 Расширенные инструкции
for
Усовершенствованный оператор
for
имеет форму:for ( Type Identifier : Expression ) Statement
Если тип
Expression
является типом массива,T[]
, то смысл расширенного оператораfor
задается следующим базовым выражениемfor
:T[] a = Expression; for (int i = 0; i < a.length; i++) { Type Identifier = a[i]; Statement }
где
a
иi
являются идентификаторами, генерируемыми компилятором, которые отличны от любых других идентификаторов (генерируемых компилятором или иным образом), которые находятся в области видимости в точке, где выполняется расширенный операторfor
.
Таким образом, на самом деле язык гарантирует, что Expression
будет оцениваться только один раз.
Для полноты здесь эквивалентность, когда Expression
имеет тип Iterable
:
JLS 14.14.2 Расширенные инструкции
for
Усовершенствованный оператор
for
имеет форму:for ( Type Identifier : Expression ) Statement
Если тип
Expression
является подтипомIterable
, то пустьi
будет типом выраженияExpression.iterator()
. Расширенное выражениеfor
эквивалентно базовому выражениюfor
формы:for (I iter = Expression.iterator(); iter.hasNext(); ) { Type Identifier = iter.next(); Statement }
где
iter
- это сгенерированный компилятором идентификатор, отличный от любых других идентификаторов (генерируемых компилятором или иным образом), которые находятся в области видимости в точке, где выполняется расширенный операторfor
.
Обратите внимание, что это ошибка времени компиляции, если Expression
не является ни Iterable
, ни массивом, поэтому приведенные выше два являются единственными случаями, когда вы можете использовать расширенный цикл for
. Кроме того, для ясности приведенные выше цитаты не содержат информации о любых меток, прикрепленных к петле for
и любых модификаторах, прикрепленных к Identifier
, но они обрабатываются, как и следовало ожидать.
О производительности расширенного цикла for
Здесь приведена цитата из Effective Java 2nd Edition, пункт 46: Предпочитают для каждой петли традиционную для циклов
Цикл for-each, введенный в версии 1.5, избавляется от беспорядка и возможности ошибки, полностью спрятав итератор или переменную индекса. Полученная идиома в равной степени относится к коллекциям и массивам. Обратите внимание, что для использования цикла for-each нет ограничений производительности даже для массивов. Фактически, он может предложить небольшое преимущество производительности перед обычным циклом
for
в некоторых случаях, поскольку он вычисляет предел индекса массива только один раз. Хотя вы можете сделать это вручную, программисты не всегда это делают.
Таким образом, в книге утверждается, что на самом деле некоторые компиляторы выходят за рамки перевода JLS и выполняют дополнительную оптимизацию для каждого цикла (при сохранении, конечно, своей семантики).
В общем, вы не должны беспокоиться о производительности для каждого цикла. Спецификация языка разумна (Expression
оценивается только один раз), и именно потому, что это предпочтительная конструкция во многих сценариях, компиляторы будут оптимизировать их как можно лучше.
См. также
- Руководство по языку Java/для каждого цикла
- Имеет примеры идиоматического использования и как это может помочь минимизировать вероятность ошибок.