Java для каждого по сравнению с постоянным клиентом для — действительно ли они эквивалентны?

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

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

19
задан Community 23 May 2017 в 11:54
поделиться

3 ответа

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

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

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;
}

Оба кода компилируются, но если вы запустите их, вы обнаружите, что первый цикл завершается нормально, а второй цикл выдает a NullPointerException .

Это означает, что они не на 100% эквивалентны! Существуют сценарии, в которых две конструкции будут вести себя по-разному!

Такие сценарии, скорее всего, будут редкими, но об этом факте не следует забывать при отладке, потому что в противном случае вы можете пропустить некоторые действительно незаметные ошибки.


В качестве дополнения обратите внимание, что иногда конструкция for-each даже не является вариантом, например если вам нужен index. Важнейший урок здесь заключается в том, что , даже если это вариант, вы должны убедиться, что это действительно эквивалентная замена, потому что это не всегда гарантируется

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

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

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

31
ответ дан 30 November 2019 в 03:43
поделиться

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

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

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

2
ответ дан 30 November 2019 в 03:43
поделиться

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

1
ответ дан 30 November 2019 в 03:43
поделиться
Другие вопросы по тегам:

Похожие вопросы: