Итераторы

Открыта

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

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

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

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

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

— Во-первых, некоторые коллекции, как например 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, 08:08
Кроме итератора есть еще интерфейс Iterable – его должны реализовывать все коллекции, которые поддерживают итератор. У него есть единственный метод: Ошибка ! Это не единственный метод. Там еще два.
Kurama
Уровень 50
29 марта 2023, 19:49
Удивительно, но Iterator - это паттерн
Виталий
Уровень 41
28 февраля 2022, 11:19
Iterator - объект, с помощью которого происходит обход коллекции Iterable - интерфейс, который есть у Iterator и с помощью которого Iterator знает как обойти данную коллекцию iterator() - метод интерфейса Iterable, который возвращает Iterator hasNext(), next(), remove() - методы класса Iterator
Dima Makarov
Уровень 42
4 мая 2023, 07:16
"методы класса Iterator" - не класса, а интерфейса
Ars
Уровень 41
27 ноября 2021, 07:06
Взять на каранда́ш - сделать мысленную или письменную заметку о ком-либо с целью в дальнейшем следить за ним, уделять ему пристальное внимание. Этот термин появился в начале XIX века, сначала в жандармерии, а потом и в других государственных ведомствах Российской империи. Дело в том, что в списках "неблагонадёжных" в Отдельном Жандармском Корпусе (Губернские Жандармские Управления и Отделения), писавшихся чернилами, заметки ставились графитовым карандашом, чтобы, при необходимости их можно было бы просто стереть. Отсюда и пошло выражение "взять на карандаш". То есть в человеке или каком-то действии есть сомнение. Подтвердится - тогда в список или доклад чернилами, а нет, тогда стереть и никто не узнает. То есть синоним - подозрение, но не имеющее доказательств. Взятие на карандаш возможности сделать свой итератор выглядит крайне не к месту...
LuneFoxJava Developer в BIFITExpert
24 февраля 2022, 14:58
Необходимость создания своего итератора выглядит довольно подозрительно - не уверен, что это стоит записывать в список своих будущих дел. И нет никаких доказательств того, что мне это точно пригодится. Возьму на карандаш…
BagbichJava Developer в CDEK
21 июня 2022, 11:45
душно
15 июля 2021, 19:09
При этом операция get будет довольно медленной, а операция next() очень быстрой.
вот только операция get в этой лекции не упоминалась. Что имел ввиду автор?
Maks PanteleevJava Developer в Bell Integrator
25 июля 2021, 13:13
get это стандартный метод получения элемента списка по индексу или по имени, речь о том, что если ты получил условный 101 элемент коллекции, а тебе нужен 102 - ты можешь вызвать метод get(102) а можешь вызывать у итератора next и скорость последнего решения будет гораздо быстрее. Если элементов миллион, то даже 100 раз подряд вызвать некст будет быстрее чем один раз гет)
LuneFoxJava Developer в BIFITExpert
24 февраля 2022, 15:03
Лучше один раз у-get-ить, чем 100 раз у-next-ить :D
alex_usJava Developer в BFT
26 января 2021, 19:03
Странный вопрос задам.. но все же... Почему бы им не перестроить теорию?)) зачем нужна эта статья на данном уровне?
Андрей ЛихтаровичBackend Developer в JazzTeam
13 февраля 2021, 19:27
1. Я например неплохо освежаю знания на таких лекциях. 2. Такие лекции закрывают дыры в обучении (когда раньше это было непонятно, а после прочтения вновь некоторые моменты становятся очевидными ) , таким образом выстраивается хорошая структурированная база знаний в голове а не просто (это-знаю ___ это не знаю )
alex_usJava Developer в BFT
13 февраля 2021, 19:49
согласен. Жаль только не сделали на 41 уровне лекцию повтора как создавать обьект и работать с примитивами. Метод принтлн там . И конечно нужно было повторить создание хелло ворд
Андрей ЛихтаровичBackend Developer в JazzTeam
18 февраля 2021, 20:58
это было-бы лучше чем просто картинка))))
2 июля 2020, 11:18
Ну про своевременность подачи материала тут уже писали... Очередное док-во того, что javarush это всё-таки не про теорию, а про практику.
Виталий Java Developer
18 апреля 2020, 16:01
Цикл for-each - это синтаксический сахар, который под капотом использует итератор, но при этом лишает нас доступа к методу remove() итератора.
Саш
Уровень 14
19 ноября 2019, 21:13
Почему 27 т.м? Лекция 7 лвл
MartyMcAir
Уровень 41
16 марта 2020, 15:09
7й lvl для квеста Collections (обычно тут пиплы с уровнями >30, т.к. Это предпоследний квест, потом Games Quest.. У каждого квеста уровни от 0 до 10.)
Ilya Sakharov
Уровень 41
23 октября 2018, 16:18
А массивы? Там тоже работает for-each, а итератора нет :) Или есть?
JustinianJudge в Mega City OneMaster
6 сентября 2019, 11:45
Судя по документации, которая различает for each для iterable объектов и отдельно массивов, получается, что итератора в массивах нет. https://stackoverflow.com/questions/35518471/in-java-8-why-were-arrays-not-given-the-foreach-method-...