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

— Привіт, Еллі!

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

Ітератори вигадали практично тоді, коли й колекції. Основне завдання колекцій було – зберігати елементи, а основне завдання ітератора – видавати ці елементи по одному.

— А що складного у тому, щоб видати набір елементів?

— По-перше, деякі колекції, наприклад 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< span class="dblue_text">>();
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() дуже швидкою.

— Хм. Переконала. Маю на увазі.

Дякую, Еллі!