В Java цикл for-each упрощает процесс обхода любой коллекции или массива, но не каждый Java-программист знает некоторые детали цикла for-each, которые будут описаны в этой статье. В отличие от других терминов, появившихся в Java 5: обобщенные псевдонимы, автоматическая инкапсуляция и переменные параметры, разработчики Java используют циклы for-each чаще, чем любую другую функцию, но когда их спрашивают, как работают расширенные циклы for-each или каковы основные требования при использовании Коллекции в цикле for-each, не каждый может на них ответить.
Это руководство и пример призваны заполнить этот пробел, углубившись в несколько интересных головоломок в циклах for-each. Хорошо, не будем вдаваться в подробности, давайте взглянем на нашу первую проблему с циклом for-each в Java5.
Расширенный цикл, вопрос 1
Рассмотрим следующий код, который обходит определенный пользователем агрегатор или класс коллекции. Что выведет этот код, выдаст ли он исключение или ошибку компилятора:
Скопируйте код кода следующим образом:
тест упаковки;
/**
* Класс Java, показывающий, как цикл for-each работает в Java.
*/
общественный класс ForEachTest {
public static void main(String args[]){
CustomCollection<String> myCollection = новая CustomCollection<String>();
myCollection.add("Java");
myCollection.add("Скала");
myCollection.add("Отличный");
//Что будет делать этот код: язык печати, выдачу исключения или ошибку времени компиляции
for(Строковый язык: myCollection){
System.out.println(язык);
}
}
}
Ниже приведен наш класс CustomCollection, который представляет собой универсальный класс, который, как и любой другой класс Collection, опирается на ArrayList и предоставляет методы для добавления и удаления элементов из коллекции.
Скопируйте код кода следующим образом:
тест упаковки;
публичный класс CustomCollection<T>{
частное ведро ArrayList<T>;
общественная CustomCollection () {
ведро = новый ArrayList();
}
общественный размер int() {
вернуть ведро.размер();
}
общедоступное логическое значение isEmpty() {
вернуть ведро.isEmpty();
}
общедоступное логическое значение содержит (T o) {
вернуть ведро.содержит(о);
}
общедоступное логическое значение add(T e) {
вернуть ведро.добавить(е);
}
общедоступное логическое удаление (T o) {
вернуть ведро.удалить(о);
}
}
Отвечать:
Приведенный выше код не скомпилируется, поскольку наш класс CustomCollection не реализует интерфейс java.lang.Iterable. Ошибка времени компиляции следующая:
Скопируйте код кода следующим образом:
Исключение в потоке "main" java.lang.RuntimeException: некомпилируемый исходный код - for-each не применим к типу выражения
требуется: массив или java.lang.Iterable
найдено: test.CustomCollection
в test.ForEachTest.main(ForEachTest.java:24)
Из этого можно извлечь интересный факт: цикл for-each применяется только к классам Java-массивов и коллекций, которые реализуют интерфейс Iterable, а поскольку все встроенные классы Collection реализуют интерфейс java.util.Collection и унаследовали Iterable, это Деталь, которую часто упускают из виду, можно увидеть в объявлении типа интерфейса Collection «публичный интерфейс Collection расширяет Iterable». Таким образом, чтобы решить вышеуказанную проблему, вы можете просто позволить CustomCollection реализовать интерфейс Collection или наследовать AbstractCollection, который является универсальной реализацией по умолчанию и показывает, как использовать абстрактные классы и интерфейсы одновременно для большей гибкости. Теперь давайте посмотрим на вторую загадку цикла for-each:
Вторая трудность с циклами for-each в Java:
Следующий пример кода вызовет исключение ConcurrentModificationException. Здесь мы используем стандартный итератор и цикл for-each для перебора ArrayList и последующего удаления элементов. Вам нужно выяснить, какой фрагмент кода вызовет ConcurrentModificationException и почему? Обратите внимание, что ответом может быть и то, и другое, ни одно, ни одно, ни другое.
Скопируйте код кода следующим образом:
тест упаковки;
импортировать java.util.ArrayList;
импортировать java.util.Collection;
импортировать java.util.Iterator;
/**
* Класс Java для демонстрации внутренней работы цикла for-each в Java.
* @автор Джавин Пол
**/
общественный класс ForEachTest2 {
public static void main(String args[]){
Список Collection<String> = новый ArrayList<String>();
list.add("Android");
list.add("iPhone");
list.add("Windows Mobile");
// Какой код выдаст исключение ConcurrentModificationException, оба,
// ни один или один из них
// пример 1
Iterator<String> itr = list.iterator();
в то время как (itr.hasNext ()) {
Строка lang = itr.next();
список.удалить (язык);
}
//пример 2
for(Строковый язык: список){
список.удалить(язык);
}
}
}
Около 70% разработчиков Java скажут, что первый блок кода выдаст исключение ConcurrentModificationException, поскольку мы не используем метод удаления итератора для удаления элементов, а используем метод удаления() из ArrayList. Однако немногие разработчики Java скажут, что та же проблема возникает с циклом for-each, поскольку мы здесь не используем итератор. Фактически, второй фрагмент кода также генерирует исключение ConcurrentModificationException, что становится очевидным после устранения первой путаницы. Поскольку цикл for-each использует внутренний итератор для обхода коллекции, он также вызывает Iterator.next(), который проверяет наличие изменений (элементов) и выдает исключение ConcurrentModificationException. Вы можете увидеть это из вывода ниже, который вы получаете, когда запускаете второй фрагмент после закомментирования первого фрагмента.
Скопируйте код кода следующим образом:
Исключение в потоке «основной» java.util.ConcurrentModificationException
в java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
в java.util.AbstractList$Itr.next(AbstractList.java:343)
в test.ForEachTest2.main(ForEachTest2.java:34)
Это все, что касается цикла for-each в Java5. Мы видели множество проблем, с которыми сталкиваются Java-программисты при написании кода для перебора класса Collection, особенно при переборе коллекции с одновременным удалением элементов. Не забывайте всегда использовать метод удаления Итератора при удалении объектов из любой коллекции (например, карты, набора или списка). Также помните, что цикл for-each — это просто синтаксический сахар (синтаксический сахар) поверх стандартного использования. только стандартный код итератора.
Примечание переводчика: Синтаксический сахар, также переводится как «сахарная грамматика» — это термин, изобретенный британским ученым-компьютерщиком Питером Дж. Лэндином и обозначающий определенный вид грамматики, добавляемой в компьютерные языки. Синтаксис не влияет на функциональность языка. язык, но облегчает его использование программистами. Вообще говоря, использование синтаксического сахара может повысить читаемость программы, тем самым уменьшая вероятность ошибок в программном коде.