Подтвердить что ты не робот

Как Java для кода цикла, сгенерированного компилятором

Как Java для кода цикла генерируется компилятором?

Например, если у меня есть:

for(String s : getStringArray() )
{
   //do something with s
}

где getStringArray() - это функция, возвращающая массив, в который я хочу включить цикл, будет ли функция вызываться всегда или только один раз? Насколько оптимален код для цикла с использованием этой конструкции вообще?

4b9b3361

Ответ 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 оценивается только один раз), и именно потому, что это предпочтительная конструкция во многих сценариях, компиляторы будут оптимизировать их как можно лучше.

См. также

Ответ 2

JDK 1.4 представил интерфейс RandomAcces. Он предназначен для того, чтобы дать подсказку алгоритмам, когда для данной реализации List более эффективно выполнять итерацию через:

for (int i=0, n=list.size(); i &lt; n; i++) {
          list.get(i);
}

чем

for (Iterator i=list.iterator(); i.hasNext(); ) {
   i.next();
}

Учитывается ли это foreach loop? Или он полностью игнорирует тот факт, что данный Iterable является фактически списком?. Следует отметить, что это означало бы добавить тест (iterable instanceof List && iterable instanceof RandomAccess) и downcast в List, что добавило бы накладные расходы, которые не всегда стоили бы этого, и можно было бы рассматривать как предварительную оптимизацию для синтаксического сахара компилятора.

Ответ 3

Компилятор может вызвать его только один раз, но вы можете зависеть от него. Это не может быть хорошей практикой кодирования. Если getStringArray() возвращает один и тот же массив каждый раз, почему бы не задать сначала переменную?

EDIT - ответ изменен с полученными комментариями.

Ответ 4

для цикла такой же, как цикл в javascript, поэтому не нужно бояться

Пример:

for(int i=0;i<10;i++)
{
    System.out.Println(i);
}