Iterator

Назначение

Iterator предоставляет унифицированный способ последовательного доступа к элементам коллекций разных структур.

Описание

Требуется получить доступ последовательно к всем элементам агрегатной структуры данных (коллекции). Для итерации по коллекции используется объект типа Iterator. Этот объект позволяет:

  • проверить, достигнут ли конец коллекции
  • получить текущий элемент коллекции
  • сдвинуть указатель текущего элемента на следующий

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

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

Iterator позволяет не выявлять все элементы сразу, а генерировать их динамически и даже создавать бесконечные коллекции. Кроме того с помощью итератора можно запомнить место в котором приостановлена итерация и возобновить её при необходимости.

Реализация

Client
Client
Клиент взаимодействует с коллекцией и итератором через интерфейс.
Iterable<E>
Iterable<E>
Интерфейс, расширяемый коллекциями, который определяет способ получение объекта Iterator для последовательного доступа к коллекции
ConcreteCollection<E>
ArrayList<E>
Конкретная коллекция с определенной структурой и определенным способом последовательного доступа к её элементам
Iterator<E>
Iterator<E>
Интерфейс, определяющий универсальные методы, необходимые для последовательного доступа к коллекции
ConcreteCollectionIterator<E>
ArrayList.Itr<E>
Конкретная реализация определенного способа доступа к элементам коллекций

Примеры

Все коллекции jdk Collection<E> реализуют интерфейс Iterable<E>. Но преимущество шаблона в том, что реализовать этот интерфейс можно самостоятельно для новой структуры данных.

Варианты

  • интерфейс Iterator может быть более широким, предоставлять методы для перехода к предыдущим элементам или перемещения по другим направлениям
  • Iterator может быть внутренним классом или внешним. Внутренний не статический имеет доступ к структуре данных и связь замыкающем объектом, но инкапсулирует позицию, указывающую на элемент данных, поэтому может создано несколько экземпляров Itertor. Внешний Iterator при создании может получать ссылку на коллекцию через конструктор
    public Iterator<E> iterator() {
      return new ConcreteIterator<E>(this);
    }
    
  • Iterator может быть копирующем состояние объекта при его создании, или защищенным через счетчик modCount - если число изменений не соответствует ожидаемому, возбуждается исключение
  • интерфейс Iterator может быть выражен 3, 2, 1 методом. Согласно принципу Command–query separation (CQS), следует разделять методы, которые выполняют команды (изменяют состояние или данные, command, “update”) и методы, которые выполняют запросы (query, “select”). Руководствуясь этим принципом правильнее было бы сделать 3 метода:
    • boolean hasNext() - query
    • void next() - command
    • E current() - query

    Но можно некоторые методы объединить в один, как например в Java boolean hasNext(), E next(). Даже можно сделать через единственный метод, который возвращает специальное значение, показывающее, что элементов больше нет или возбуждает исключение.

Чем отличается

Factory Method Iterator является примером реализации Factory Method. Интерфейс Iterable является интерфейсом фабрики, а продукт фабрики – сами итераторы Iterator.

Adapter Iterator является адаптером, который адаптирует коллекцию к интерфейсу последовательного перебора.

Ссылки

https://java-design-patterns.com/patterns/iterator/

https://github.com/iluwatar/java-design-patterns/tree/master/iterator

https://refactoring.guru/design-patterns/iterator

Iterator Pattern – Design Patterns (ep 16)

Command–query separation (CQS)