Паттерн "Итератор" в Java — Iterable, пагинация и for-each
Обзор GoF — в поведенческих паттернах. Аналог на C# (yield, LINQ) — в Итератор в C#. Здесь — Java: Iterable / Iterator, enhanced for и итератор, который скрывает пагинацию из БД.
Задача паттерна
Iterator даёт последовательный обход элементов без раскрытия внутреннего устройства коллекции (массив, дерево, курсор в БД). Клиент работает через "окно" hasNext() / next(), не зная, что за ним.
В Java контракт зафиксирован в JDK:
| Тип | Роль |
|---|---|
Iterable<T> | Коллекция умеет выдать итератор (iterator()) |
Iterator<T> | hasNext(), next(), remove() (опционально) |
for (T x : coll) | Синтаксический сахар над Iterable |
Пагинация из БД — свой Iterator
Клиент обходит пользователей как обычную последовательность; подгрузка страниц спрятана внутри итератора.
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.function.Function;
public class PaginatedIterator<T> implements Iterator<T> {
private final Function<Integer, List<T>> pageFetcher;
private final int pageSize;
private List<T> currentPage;
private int pageIndex = 0;
private int itemIndex = 0;
public PaginatedIterator(Function<Integer, List<T>> pageFetcher, int pageSize) {
this.pageFetcher = pageFetcher;
this.pageSize = pageSize;
this.currentPage = pageFetcher.apply(0);
}
@Override
public boolean hasNext() {
if (itemIndex < currentPage.size()) {
return true;
}
if (currentPage.size() < pageSize) {
return false;
}
pageIndex++;
currentPage = pageFetcher.apply(pageIndex);
itemIndex = 0;
return !currentPage.isEmpty();
}
@Override
public T next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return currentPage.get(itemIndex++);
}
}
Использование (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 — fail-fast защита от "битого" состояния.
Для одноразового ленивого пайплайна чаще уместен Stream (Stream API). Собственный Iterator оправдан, когда нужна совместимость с legacy-кодом, точный контроль страниц или обход вне стандартных коллекций.
См. также
См. также
Другие статьи этого же раздела в боковом меню (как на странице "О разделе"). Паттерн — это повторяющийся шаблон, узор или схема. Паттерны встречаются повсюду — в природе, архитектуре, поведении людей и, конечно, в программировании. Порождающие паттерны проектирования — это группа шаблонов, направленных на решение задач, связанных с созданием объектов. Структурные паттерны — это группа шаблонов проектирования, решающих задачи организации классов и объектов таким образом, чтобы обеспечить гибкую архитектуру программного обеспечения. Поведенческие паттерны — это группа шаблонов проектирования, которые определяют способы взаимодействия объектов и распределения ответственности между ними. Архитектурные паттерны — это проверенные решения для организации структуры программного обеспечения. Интеграция систем — одна из центральных задач в современной разработке программного обеспечения. Паттерны доменного моделирования представляют собой проверенные решения для организации бизнес-логики в программных системах. Паттерн Strategy в C# — классическая реализация через интерфейс, замена на Func и Action, DI и критерии выбора без лишних абстракций. Паттерн Iterator в C# — ручной IEnumerator, генерация итератора компилятором через yield return, ленивость, LINQ и случаи, когда класс писать всё же нужно. Abstract Factory в C# и .NET — классическая схема через интерфейсы, замена через DI-контейнер, фабричный делегат и keyed services в .NET 8. Паттерн Command в C# — классическая схема, делегаты, MediatR, очередь задач, undo и критерии выбора между объектом команды и простым вызовом сервиса. Паттерн Observer в C# — event и делегаты, IObservable IObserver, слабая связанность, отписка и как не поймать утечки памяти в долгоживущих сервисах.Обзор паттернов проектирования
Порождающие паттерны
Структурные паттерны
Поведенческие паттерны
Архитектурные паттерны
Паттерны интеграции внешних систем
Паттерны проектирования доменных моделей
Стратегия в C#
Итератор в C#
Фабрика в C#
Команда в C#
Наблюдатель в C#