Link Search Menu Expand Document

The Iterator Design Pattern

The Iterator Design Pattern is designed to give access to the elements of an aggregate object sequentially without exposing their underlying representation.


The Iterator Design Pattern is also known as Cursor.

What problems does it solve?

You can travers/ access elements of aggregate objects (like custom collections) sequentially without exposing their internal structure. The actual traversing algorithm will be implemented in the aggregate objects.

Glossary:

  • Aggregate - aggregate object or collection, which elements we want to iterate (list or some other “bag” with elements)
  • Concrete Aggregate - specific aggregate object
  • Iterator - the generic interface that allows us to iterate over the elements of the aggregate
  • Concrete Iterator - specific iterator for the specific aggregate object

Pros:

  • Hides the implementation of traversing
  • Decouples collection classes and algorithms
  • More flexible than internal iterators (i.e. you can implement an iterator that traverses elements backward or based on another specification or to traverse 2 collections at the same time)

Cons:

  • The iterator may not be aware if elements change during the iteration
  • It can be an overkill if you can actually use the internal iterators instead of creating you own external iterator

How to recognize it?

When you call a behavioral method and it sequentially returns instances of elements (from a different type) from some aggregate structure (list/ container).

Examples from Java API

All implementations of java.util.Iterator (thus among others also java.util.Scanner!).
All implementations of java.util.Enumeration

Scenarios

  • When you need to traverse some custom collection of elements sequentially, based on specific business rule
  • When you need multiple iterators that know how to travers elements of a custom collection
  • When you need more complex way to traverse elements, than the provided inner iterators

Example 1

Let’s say we have a collection of books. Each book has a title and genre. We may want to iterate our collection by some keyword in the title or by genre. This can be done by implementing two different iterators. Let’s do it.

Source Code

1). Create the Book class (this is the type of the elements of the aggregate type) and the Book Genre enum

@ToString
public class Book {
    @Getter
    private String title;
    @Getter
    private BookGenre genre;

    public Book(String title, BookGenre genre) {
        this.title = title;
        this.genre = genre;
    }
}
public enum BookGenre {
    MYSTERY,
    FANTASY,
    POETRY
}

2). We need a generic iterator that will iterate through the collection elements

public interface BookIterator {

    boolean hasNext();

    Book next();
}

3). We need an interface for the book collection (out aggregate object)

public interface BookCollection {

    void add(Book book);

    void remove(Book book);

    void clear();

    BookIterator keywordIterator(String keyword);

    BookIterator genreIterator(BookGenre genre);
}

4). We need an implementation of the collection. This is the place, where we are going to implement the both iterators. We will place them as private inner classes, in order not to expose the traversal.

public class BookCollectionImpl implements BookCollection {

    private List<Book> elements = new ArrayList<>();

    @Override
    public void add(Book book) {
        this.elements.add(book);
    }

    @Override
    public void remove(Book book) {
        this.elements.remove(book);
    }

    @Override
    public void clear() {
        this.elements.clear();
    }

    @Override
    public BookIterator keywordIterator(String keyword) {
        return new BookIteratorByKeyword(new ArrayList<>(this.elements), keyword);
    }

    @Override
    public BookIterator genreIterator(BookGenre genre) {
        return new BookIteratorByGenre(new ArrayList<>(this.elements), genre);
    }

    private class BookIteratorByKeyword implements BookIterator {

        private List<Book> elements;

        private String keyword;

        private int position = 0;

        public BookIteratorByKeyword(List<Book> elements, String keyword) {
            this.elements = elements;
            this.keyword = keyword.toLowerCase();
        }

        @Override
        public boolean hasNext() {
            while (this.position < this.elements.size()) {
                Book b = this.elements.get(this.position);
                if (b.getTitle().toLowerCase().contains(this.keyword)) {
                    return true;
                }
                this.position++;
            }
            return false;
        }

        @Override
        public Book next() {
            Book book = null;
            if (this.position >= 0 && this.position < this.elements.size()) {
                book = this.elements.get(this.position);
                this.position++;
            }
            return book;
        }
    }

    private class BookIteratorByGenre implements BookIterator {

        private List<Book> elements;

        private BookGenre genre;

        private int position = 0;

        public BookIteratorByGenre(List<Book> elements, BookGenre genre) {
            this.elements = elements;
            this.genre = genre;
        }

        @Override
        public boolean hasNext() {
            while (this.position < this.elements.size()) {
                Book b = this.elements.get(this.position);
                if (this.genre.equals(b.getGenre())) {
                    return true;
                }
                this.position++;
            }
            return false;
        }

        @Override
        public Book next() {
            Book book = null;
            if (this.position >= 0 && this.position < this.elements.size()) {
                book = this.elements.get(this.position);
                this.position++;
            }
            return book;
        }
    }
}

5). A Demo class

public class _Main {

    public static void main(String[] args) {
        BookCollection collection = new BookCollectionImpl();
        collection.add(new Book("The Ruin of Kings /A Chorus of Dragons, #1/ by Jenn Lyons", BookGenre.FANTASY));
        collection.add(new Book("Black Leopard, Red Wolf (The Dark Star Trilogy)", BookGenre.FANTASY));
        collection.add(new Book("The Bird King by G. Willow Wilson - Goodreads", BookGenre.FANTASY));
        collection.add(new Book("The Strength In Our Scars, by Bianca Sparacino", BookGenre.POETRY));
        collection.add(new Book("End of Watch /Bill Hodges Trilogy, #3/ by Stephen King", BookGenre.MYSTERY));
        collection.add(new Book("The Bird King by G. Willow Wilson - Goodreads", BookGenre.MYSTERY));

        System.out.println("========== Keyword Iterator - \"King\" =========");
        BookIterator keywordIt = collection.keywordIterator("King");
        traverseCollection(keywordIt);

        System.out.println("========== Genre Iterator - \"Mystery\" =========");
        BookIterator mysteryIt = collection.genreIterator(BookGenre.MYSTERY);
        traverseCollection(mysteryIt);

        System.out.println("========== Genre Iterator - \"Fantasy\" =========");
        BookIterator fantasyIt = collection.genreIterator(BookGenre.FANTASY);
        traverseCollection(fantasyIt);
    }

    private static void traverseCollection(BookIterator iterator) {
        Book book;
        while (iterator.hasNext()) {
            book = iterator.next();
            if (book != null) {
                System.out.println(book);
            }
        }
        System.out.println();
    }
}

Output:

========== Keyword Iterator - "King" =========
Book(title=The Ruin of Kings /A Chorus of Dragons, #1/ by Jenn Lyons, genre=FANTASY)
Book(title=The Bird King by G. Willow Wilson - Goodreads, genre=FANTASY)
Book(title=End of Watch /Bill Hodges Trilogy, #3/ by Stephen King, genre=MYSTERY)
Book(title=The Bird King by G. Willow Wilson - Goodreads, genre=MYSTERY)

========== Genre Iterator - "Mystery" =========
Book(title=End of Watch /Bill Hodges Trilogy, #3/ by Stephen King, genre=MYSTERY)
Book(title=The Bird King by G. Willow Wilson - Goodreads, genre=MYSTERY)

========== Genre Iterator - "Fantasy" =========
Book(title=The Ruin of Kings /A Chorus of Dragons, #1/ by Jenn Lyons, genre=FANTASY)
Book(title=Black Leopard, Red Wolf (The Dark Star Trilogy), genre=FANTASY)
Book(title=The Bird King by G. Willow Wilson - Goodreads, genre=FANTASY)