Ответ 1
Хотя часто две конструкции взаимозаменяемы, ОНИ НЕ НАХОДЯТСЯ НА 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 и позже понимаете, что вам нужно переключиться на цикл индексированных, убедитесь, что вы сохраняете семантику, потому что она не гарантируется.
В частности, опасайтесь какой-либо модификации ссылки на массив/коллекцию, которая является итерированной_ (модификация содержимого может/не может инициировать ConcurrentModificationException
, но это другая проблема).
Гарантирование сохранения семантики также намного сложнее, если вы используете коллекции, которые используют пользовательские итераторы, но, как показывает этот пример, две конструкции различаются, даже если задействованы простые массивы.