Перейти к основному содержимому

Паттерн "Итератор" в Java — Iterable, пагинация и for-each

Разработчику Архитектору
Теория данных (раздел 3)

Обзор GoF — в поведенческих паттернах. Аналог на C# (yield, LINQ) — в Итератор в C#. Здесь — JavaIterable / Iterator, enhanced for и итератор, который скрывает пагинацию из БД.

Play ITЗагрузка интерактивного демо…


Задача паттерна

Iterator даёт последовательный обход элементов без раскрытия внутреннего устройства коллекции (массив, дерево, курсор в БД). Клиент работает через "окно" hasNext() / next(), не зная, что за ним.

В Java контракт зафиксирован в JDK:

ТипРоль
Iterable<T>Коллекция умеет выдать итератор (iterator())
Iterator<T>hasNext(), next(), remove() (опционально)
for (T x : coll)Синтаксический сахар над Iterable

Пагинация из БД — свой Iterator

Клиент обходит пользователей как обычную последовательность; подгрузка страниц спрятана внутри итератора.

Код ITЗагрузка примера кода…

Использование (Spring Data в лямбде — пример):

Iterator<User> users = new PaginatedIterator<>(
page -> userRepository.findAll(PageRequest.of(page, 50)).getContent(),
50
);

while (users.hasNext()) {
process(users.next());
}

Тот же сценарий можно выразить через Stream и Spliterator, но явный Iterator нагляден и совместим с любым кодом, ожидающим Iterable.


Когда применять

СитуацияЗачем Iterator
Разные структуры, один способ обходаЕдиный контракт для списка, дерева, БД
Сложный обход (пагинация, ленивая подгрузка)Детали не утекают в клиент
Несколько независимых проходовУ каждого вызова iterator() своё состояние

Риски

Для простого ArrayList отдельный класс итератора избыточен — достаточно встроенного API и for-each.

При изменении коллекции во время обхода (кроме Iterator.remove()) JVM бросает ConcurrentModificationException. Итератор сверяет modCount с ожидаемым значением и останавливает обход. Подробный разбор — fail-fast и fail-safe итераторы; про гонки потоков — многопоточность в Java.

Iterator и Stream

Для одноразового ленивого пайплайна чаще уместен Stream (Stream API).

Собственный Iterator оправдан, когда нужна совместимость с legacy-кодом, точный контроль страниц или обход вне стандартных коллекций.


См. также