Как Java для кода цикла, сгенерированного компилятором - PullRequest
16 голосов
/ 08 августа 2010

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

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

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

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

Ответы [ 4 ]

53 голосов
/ 08 августа 2010

О семантике расширенной for петли

Вот соответствующие выдержки из Спецификации языка Java, 3-е издание , слегка отредактированные для ясности:

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, Item 46: Предпочитайте циклы for-each вместо традиционных циклов for

Цикл for-each, представленный в выпуске 1.5, избавляет от беспорядка и возможности ошибки, полностью скрывая итератор или индексную переменную. Получающаяся идиома в равной степени относится к коллекциям и массивам. Обратите внимание, что при использовании цикла for-each не снижается производительность даже для массивов. Фактически, он может предложить небольшое преимущество в производительности по сравнению с обычным циклом for в некоторых случаях, так как он вычисляет ограничение индекса массива только один раз. Хотя вы можете делать это вручную, программисты не всегда делают это.

Таким образом, в книге утверждается, что на самом деле некоторые компиляторы выходят за рамки преобразования JLS и выполняют дополнительную оптимизацию для цикла for-each (при этом, конечно, сохраняя его семантику).

Таким образом, вам не нужно беспокоиться о производительности цикла for-each. Спецификация языка разумна (Expression оценивается только один раз), и именно потому, что это предпочтительная конструкция во многих сценариях , компиляторы позаботятся о том, чтобы оптимизировать их как можно лучше.

Смотри также

1 голос
/ 10 декабря 2010

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? Или он полностью игнорирует тот факт, что данный Iterable фактически является списком? Следует отметить, что это подразумевало бы добавление теста (iterable instanceof List && iterable instanceof RandomAccess) и понижения рейтинга в List, что добавило бы издержки, которые не всегда стоят того, и их можно было бы считать преждевременной оптимизацией для функции компилятора синтаксический сахар .

0 голосов
/ 13 февраля 2014

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

пример:

for(int i=0;i<10;i++)
{
    System.out.Println(i);
}
0 голосов
/ 08 августа 2010

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

РЕДАКТИРОВАТЬ - ответ изменен с полученными комментариями.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...