JavaRush /Курсы /Java Collections /Итераторы

Итераторы

Java Collections
7 уровень , 3 лекция
Открыта

— Привет, Амиго!

— Привет, Элли!

— Сегодня я хочу рассказать тебе про итераторы.

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

— А что сложного в том, чтобы выдать набор элементов?

— Во-первых, некоторые коллекции, как например Set не имеют установленного порядка элементов и/или он постоянно меняется.

Во-вторых, некоторые структуры данных могут хранить объекты очень сложно: различными группами, списками и т.д. Т.е. задача отдать последовательно все элементыбудет сложной и нетривиальной.

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

— М-да.

— Вот! Именно такие проблемы должен был решить итератор. Итератор – это специальный внутренний объект в коллекции, который с одной стороны имеет доступ ко всем ее private данным и знает ее внутреннюю структуру, с другой – реализует общедоступный интерфейс Iterator, благодаря чему все знают, как с ним работать.

Некоторые итераторы имеют внутри себя массив, куда копируются все элементы коллекции во время создания итератора. Это гарантирует, что последующее изменение коллекции не повлияет на порядок и количество элементов.

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

В новых коллекциях, добавленных в библиотеке concurrency, устройство итератора переработано, поэтому там такой проблемы нет.

Давай я тебе напомню, как устроен итератор.

В Java есть специальный интерфейс Iterator, вот какие у него методы:

Методы интерфейса Iterator<E> Описание
boolean hasNext() Проверяет, есть ли еще элементы
E next() Возвращает текущий элемент и переключается на следующий.
void remove() Удаляет текущий элемент

Итератор позволяет поочередно получить все элементы коллекции. Логичнее представить итератор чем-то вроде InputStream – у него есть все данные, но его задача выдавать их последовательно.

Метод next() возвращает следующий (очередной) элемент коллекции.

Метод hasNext() используется, чтобы проверять, есть ли еще элементы.

Ну, а remove() – удаляет текущий элемент.

Вопросы есть?

— А почему методы называются так странно? Почему не isEmpty() или getNextElement()?

Разве так не логичнее?

— Логичнее, но такие названия пришли из языка C++, где итераторы появились раньше.

— Ясно. Продолжим.

Кроме итератора есть еще интерфейс Iterable – его должны реализовывать все коллекции, которые поддерживают итератор. У него есть единственный метод:

Методы interface Iterable<T> Описание
Iterator<T>iterator() Возвращает объект-итератор

С помощью этого метода у любой коллекции можно получить объект итератор для обхода ее элементов. Давай обойдем все элементы дерева в коллекции TreeSet:

Пример
TreeSet<String> set = new TreeSet<String>();
Iterator<String> iterator = set.iterator();

while (iterator.hasNext())
{
 String item = iterator.next();
 System.out.println(item);
}

Такое использование итератора не очень удобно – слишком много лишнего и очевидного кода. Ситуация упростилась, когда в Java появился цикл по итератору – for-each.

Теперь такой код гораздо компактнее и читабельнее:

Было Стало
TreeSet<String> set = new TreeSet<String>();
Iterator<String> iterator = set.iterator();

while (iterator.hasNext())
{
 String item = iterator.next();
 System.out.println(item);
}
TreeSet<String> set = new TreeSet<String>();

for(String item : set)
{
 System.out.println(item);
}

Это один и тот же код! Итератор используется и там, и там.

Просто в цикле for-each его использование скрыто. Обрати внимание – в коде справа вообще нет красного цвета. Использование итератора скрыто полностью.

Цикл for-each можно использовать для любых объектов, которые поддерживают итератор. Т.е. ты можешь написать свой класс, добавить ему метод iterator() и сможешь использовать его объекты в правой части конструкции for-each.

— Ого! Я, конечно, не рвусь писать собственные коллекции и итераторы, но предложение все равно заманчивое. Возьму на карандаш.

— Кроме того, есть еще одна популярная разновидность итераторов, для которой даже придумали свой интерфейс. Речь идет об итераторе для списков – ListIterator.

Списки, независимо от реализации, обладают порядком элементов, что в свою очередь позволяет работать с ними через итератор чуть более удобно.

Вот какие методы есть у интерфейса ListIterator<E>:

Метод Описание
boolean hasNext() Проверяет, есть ли еще элементы впереди.
E next() Возвращает следующий элемент.
int nextIndex() Возвращает индекс следующего элемента
void set(E e) Меняет значение текущего элемента
boolean hasPrevious() Проверяет, есть ли элементы позади.
E previous() Возвращает предыдущий элемент
int previousIndex() Возвращает индекс предыдущего элемента
void remove() Удаляет текущий элемент
void add(E e) Добавляет элемент в список.

Т.е. тут мы можем ходить не только вперед, но и назад. И еще пара фич по мелочи.

— Что ж, интересная штука. А где его используют?

— Например, ты хочешь двигаться туда-обратно по связному списку. При этом операция get будет довольно медленной, а операция next() очень быстрой.

— Хм. Убедила. Буду иметь ввиду.

Спасибо, Элли!

Комментарии (28)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Rolik Уровень 41
8 июня 2023
Кроме итератора есть еще интерфейс Iterable – его должны реализовывать все коллекции, которые поддерживают итератор. У него есть единственный метод: Ошибка ! Это не единственный метод. Там еще два.
Kurama Уровень 50
29 марта 2023
Удивительно, но Iterator - это паттерн
Виталий Уровень 41
28 февраля 2022
Iterator - объект, с помощью которого происходит обход коллекции Iterable - интерфейс, который есть у Iterator и с помощью которого Iterator знает как обойти данную коллекцию iterator() - метод интерфейса Iterable, который возвращает Iterator hasNext(), next(), remove() - методы класса Iterator
Dima Makarov Уровень 42
4 мая 2023
"методы класса Iterator" - не класса, а интерфейса
Ars Уровень 41
27 ноября 2021
Взять на каранда́ш - сделать мысленную или письменную заметку о ком-либо с целью в дальнейшем следить за ним, уделять ему пристальное внимание. Этот термин появился в начале XIX века, сначала в жандармерии, а потом и в других государственных ведомствах Российской империи. Дело в том, что в списках "неблагонадёжных" в Отдельном Жандармском Корпусе (Губернские Жандармские Управления и Отделения), писавшихся чернилами, заметки ставились графитовым карандашом, чтобы, при необходимости их можно было бы просто стереть. Отсюда и пошло выражение "взять на карандаш". То есть в человеке или каком-то действии есть сомнение. Подтвердится - тогда в список или доклад чернилами, а нет, тогда стереть и никто не узнает. То есть синоним - подозрение, но не имеющее доказательств. Взятие на карандаш возможности сделать свой итератор выглядит крайне не к месту...
LuneFox Уровень 41 Expert
24 февраля 2022
Необходимость создания своего итератора выглядит довольно подозрительно - не уверен, что это стоит записывать в список своих будущих дел. И нет никаких доказательств того, что мне это точно пригодится. Возьму на карандаш…
Bagbich Уровень 1
21 июня 2022
душно
15 июля 2021

При этом операция get будет довольно медленной, а операция next() очень быстрой.
вот только операция get в этой лекции не упоминалась. Что имел ввиду автор?
Maks Panteleev Уровень 41
25 июля 2021
get это стандартный метод получения элемента списка по индексу или по имени, речь о том, что если ты получил условный 101 элемент коллекции, а тебе нужен 102 - ты можешь вызвать метод get(102) а можешь вызывать у итератора next и скорость последнего решения будет гораздо быстрее. Если элементов миллион, то даже 100 раз подряд вызвать некст будет быстрее чем один раз гет)
LuneFox Уровень 41 Expert
24 февраля 2022
Лучше один раз у-get-ить, чем 100 раз у-next-ить :D
alex_us Уровень 20
26 января 2021
Странный вопрос задам.. но все же... Почему бы им не перестроить теорию?)) зачем нужна эта статья на данном уровне?
13 февраля 2021
1. Я например неплохо освежаю знания на таких лекциях. 2. Такие лекции закрывают дыры в обучении (когда раньше это было непонятно, а после прочтения вновь некоторые моменты становятся очевидными ) , таким образом выстраивается хорошая структурированная база знаний в голове а не просто (это-знаю ___ это не знаю )
alex_us Уровень 20
13 февраля 2021
согласен. Жаль только не сделали на 41 уровне лекцию повтора как создавать обьект и работать с примитивами. Метод принтлн там . И конечно нужно было повторить создание хелло ворд
18 февраля 2021
это было-бы лучше чем просто картинка))))
Роман Тарнакин Уровень 35
2 июля 2020
Ну про своевременность подачи материала тут уже писали... Очередное док-во того, что javarush это всё-таки не про теорию, а про практику.
Виталий Уровень 41
18 апреля 2020
Цикл for-each - это синтаксический сахар, который под капотом использует итератор, но при этом лишает нас доступа к методу remove() итератора.
Саш Уровень 14
19 ноября 2019
Почему 27 т.м? Лекция 7 лвл
MartyMcAir Уровень 41
16 марта 2020
7й lvl для квеста Collections (обычно тут пиплы с уровнями >30, т.к. Это предпоследний квест, потом Games Quest.. У каждого квеста уровни от 0 до 10.)
Ilya Sakharov Уровень 41
23 октября 2018
А массивы? Там тоже работает for-each, а итератора нет :) Или есть?
Justinian Уровень 41 Master
6 сентября 2019
Судя по документации, которая различает for each для iterable объектов и отдельно массивов, получается, что итератора в массивах нет. https://stackoverflow.com/questions/35518471/in-java-8-why-were-arrays-not-given-the-foreach-method-...