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