


1. Предыстория появления итератора
Вы уже познакомились со множеством HashSet
. И если вы действительно разбирались с ним, а не просто читали лекцию, должны были задаться вопросом:
А как вывести список всех элементов HashSet на экран? Ведь методов get()
и set()
у множества нет!
И HashSet
в своей проблеме не одинок. Кроме HashSet
, есть еще много различных коллекций, у которых нельзя получить элемент по номеру, ведь четкий порядок элементов отсутствует.
Программисты в свое время изобрели много сложных структур данных, таких как граф, дерево. Или, например, список списков.
Многие контейнеры меняют порядок своих элементов при добавлении новых или удалении существующих элементов. Например, есть список, который хранит элементы в отсортированном порядке, и при добавлении нового элемента он практически всегда вставляется в середину списка.
Вот мы и получаем ситуацию, что контейнер, содержащий элементы, есть, сами элементы тоже есть, а фиксированного порядка нет.
Но допустим, мы хотим скопировать все элементы из такой коллекции в массив или список. Нам нужно получить все элементы. Нам все равно в каком порядке мы их обойдем, главное — не повторяться. Как нам это сделать?
2. Итератор в коллекции
В качестве решения вышеописанной проблемы было предложено решение — итератор.
Итератор — это специальный объект у коллекции, который помогает обойти все элементы коллекции и не повторяться.
Получить итератор у любой коллекции можно с помощью кода:
Iterator<Тип> it = имя.iterator();
Где имя
— это имя переменной-коллекции, Тип
— это тип элементов коллекции. iterator()
— это метод коллекции. it
— это имя переменной-объекта-итератора.
У объекта-итератора есть 3 метода:
Метод | Описание |
---|---|
|
Возвращает очередной элемент коллекции |
|
Проверяет, есть ли еще не пройденные элементы |
|
Удаляет текущий элемент коллекции |
Эти методы чем-то похожи на методы класса Scanner: nextInt()
и hasNextInt()
.
Метод next()
возвращает очередной элемент коллекции, у которой мы получили итератор.
Метод hasNext()
проверяет, есть ли еще элементы в коллекции, которые итератор не вернул.
Вот как можно вывести на экран все элементы множества HashSet
:
Код | Примечания |
---|---|
|
Создаем объект типа HashSet , который хранит элементы типа String .Заносим в set приветствия на разных языках.Получаем объект-итератор у множества set .Пока есть еще элементы Получаем следующий элемент Выводим элемент на экран |
3. Цикл for-each
Основной минус итератора в том, что с его использованием код получается еще более громоздким, чем с использованием цикла for
.
Давайте для сравнения выведем на экран список с помощью цикла for
и с помощью итератора:
Итератор | Цикл for |
---|---|
|
|
Да, элементы списка ArrayList
гораздо лучше обходить с помощью цикла — все короче получается.
Однако разработчики Java вновь решили подсыпать нам сахарку. И на наше счастье, это опять был синтаксический сахар.
Они добавили в Java новый вид циклов и назвали его for-each
. Вот как выглядит его использование в общем случае:
for(Тип имя:коллекция)
Где коллекция
— это имя переменной коллекции, Тип
— это тип элементов коллекции, а имя
— это имя переменной, которая на каждом витке цикла принимает очередное значение из коллекции.
Этот цикл обходит все элементы коллекции с помощью скрытого итератора. Вот как он на самом деле работает:
Цикл for-each | Что видит компилятор: Цикл с итератором |
---|---|
|
|
Когда компилятор встретит в вашем коде цикл for-each
, он просто заменит его на код справа: добавит метод получения итератора и все недостающие вызовы методов.
Программисты очень любят цикл for-each
и практически всегда используют его, когда нужно обойти все элементы коллекции.
Даже обход списка ArrayList
с помощью цикла for-each
выглядит короче:
Цикл for-each | Цикл for |
---|---|
|
|
4. Удаление элемента в цикле for-each
У цикла for-each
есть один минус: он не умеет правильно удалять элементы. Если вы напишете такой код, получите ошибку.
Код | Примечание |
---|---|
|
При удалении возникнет ошибка! |
Это очень красивый и понятный код, только работать он не будет.
Нельзя менять коллекцию, пока вы обходите ее с помощью итератора.
Есть три способа обойти это ограничение.
1 Использование другого цикла
Если вы обходите коллекцию ArrayList
, можете воспользоваться обычным циклом со счетчиком i
.
Код |
---|
|
Однако этот вариант не подходит для коллекций HashSet
и HashMap
2 Явное использование итератора
Можно использовать итератор явно и задействовать его метод remove()
.
Рабочий вариант | Нерабочий вариант |
---|---|
|
|
Обратите внимание, что метод remove()
мы вызываем у объекта-итератора! Итератор «будет знать» об удалении элемента и сможет правильно обработать эту ситуацию.
3 Использование копии коллекции
Также вы можете создать копию коллекции и использовать в цикле for-each
коллекцию-копию, а удалять элементы из оригинальной коллекции.
Код | Примечание |
---|---|
|
Копию коллекции создать очень легко Цикл использует итератор коллекции-копии. Элементы удаляются из коллекции list .
|
Копия коллекции создается довольно быстро: элементы при копировании коллекции не дублируются, в новой коллекции будут храниться ссылки на те же элементы, что и в старой.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ