Java для каждого против обычного для - они эквивалентны? - PullRequest
19 голосов
/ 11 марта 2010

Эти две конструкции эквивалентны?

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: эти два кода одинаковы ). Мой ответ там оказался не таким, но я чувствую, что точная семантика Java для каждого имеет некоторые нюансы, на которые нужно обратить внимание.

Ответы [ 4 ]

31 голосов
/ 11 марта 2010

В то время как часто эти две конструкции взаимозаменяемы, ОНИ НЕ 100% ЭКВИВАЛЕНТНЫ * !!!

Доказательство можно построить, определив // code goes here, что приведет к тому, что две конструкции будут вести себя по-разному. Одно из таких тел цикла:

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

В частности, _ будьте осторожны с любой модификацией ссылка на повторяющегося массива / коллекции_ (модификация , содержимое может / не может вызывать ConcurrentModificationException, но это другая проблема).

Гарантировать сохранение семантики также намного сложнее, когда вы используете коллекции, использующие пользовательские итераторы, но, как показывает этот пример, эти две конструкции различаются даже при использовании простых массивов.

2 голосов
/ 11 марта 2010

Они в значительной степени эквивалентны . Но может быть несколько случаев, когда их нет. Лучше всего сделать это final.

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

Даже тогда вы можете сделать i-- во втором цикле. Если вы не сделаете некоторые из этих невероятных вещей, они в основном эквивалентны.

1 голос
/ 17 апреля 2017

Обратите внимание, что самое важное различие подразумевается в ответе, который дали «полигеномасляные вещества», но не указано явно: for-each перебирает массив, но вы не можете изменить ни один из элементов, используя экземпляр, который дает цикл Вы (в этом случае, переменная "char x"). Классический цикл позволяет использовать индекс и изменять элемент массива.

Редактировать: быстрая коррекция.

1 голос
/ 11 марта 2010

Первый более читабелен и поэтому более предпочтителен, если вам все равно понадобится индекс, а затем второй.

...