Паттерн "Итератор" в Java — Iterable, пагинация и for-each
Модель и масштаб — Основы БД, опорные темы, проектирование БД, пакетная работа. Карта — о разделе.
Обзор GoF — в поведенческих паттернах. Аналог на C# (yield, LINQ) — в Итератор в C#. Здесь — Java — Iterable / 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.
Для одноразового ленивого пайплайна чаще уместен Stream (Stream API).
Собственный Iterator оправдан, когда нужна совместимость с legacy-кодом, точный контроль страниц или обход вне стандартных коллекций.
См. также
- Поведенческие паттерны — Итератор
- Итератор в C#
- Коллекции Java — fail-fast и fail-safe итераторы
- cursor/keyset в REST API — шесть схем пагинации