Iterator Design Pattern with Java

Iterator Design Pattern
Iterator Design Pattern

Most of the time we use the Iterator from the Collections API. It’s very important to know how to implement a customized Iterator in order to understand what is happening behind the scenes. If we read Java source code, for example, we are going to find out that the elements from ArrayList are actually manipulated in a normal array. If we read Java source code we can greatly improve our programming skills. I strongly recommend that you make this a habit.

Iterator diagram:

iterator_diagram.PNG

Get the Design_Patterns_Saga_GitHub_Source_Code

1 – Iterator: This is the main class where we are able to iterate the objects. The first detail to be aware of is that we are implementing the Iterable interface using String as a generic type.

We are declaring an array of products and the index.

In the constructor, we are initializing the array of products with the size of 10 and assigning zero to the index.

In the add method, we are adding a new element inside the array. We simply check if the index is the same size as the initial size of the array, in this case, 10. If so, we are going to make the array 5 times larger than before. Following this process, we copy the old array information to the largerArray. Finally, we add the product to the final array.

In the iterator method, we are using an anonymous inner class that implements Iterator. If we are using the Iterator interface we must implement all of its methods.

Again, we are declaring the index to control the iteration.

Let’s see what happens on the Iterator methods:

hasNext: verifies if there is another element to be iterated. If the index from the iterator is lower than the size of the array and the element is different from null it means we have the next element.

next: gets the next element from the products. It simply refers to the next element from the array and returns it.

remove: it copies the old array without the removed position, returning the removed element.

import java.util.Iterator;

public class ProductRepository implements Iterable<String> {

  private String[] products;
  private int index;

  public ProductRepository() {
    products = new String[10];
    index = 0;
  }

  public void add(String product) {
    if (index == products.length) {
      String[] largerProducts = new String[
          products.length + 5];
      System.arraycopy(products, 0, largerProducts,
          0, products.length);
      products = largerProducts;
      largerProducts = null;
    }

    products[index++] = product;
  }

  @Override
  public Iterator<String> iterator() {
    return new Iterator<String>() {

      private int currentIndex = 0;

      @Override
      public boolean hasNext() {
        return currentIndex < products.length &&
            products[currentIndex] != null;
      }

      @Override
      public String next() {
        return products[currentIndex++];
      }

      @Override
      public void remove() {
        System.arraycopy(products, currentIndex + 1,
            products, currentIndex, products.length - 1);
      }
    };
  }
}

2 – Unit Test: Now we are going to test the Iterator.

customIteratorTest: we add some elements, we get the iterator, remove two elements and check if the remaining element is “Tablet”.

javaAPIIteratorTest: we are just using the iterator from the Java Collections API, removing all the elements and checking if the Set Collection is empty.

public class IteratorTest {

  @Test
  public void customIteratorTest() {
    ProductRepository repo = new ProductRepository();

    repo.add("Guitar");
    repo.add("Notebook");
    repo.add("Tablet");

    Iterator<String> productIterator = repo.iterator();
    productIterator.remove();
    productIterator.remove();

    Assert.assertEquals(productIterator.next(), "Tablet");
  }

  @Test
  public void javaAPIIteratorTest() {
    Set<String> simpsons = new HashSet<>();

    simpsons.add("Homer");
    simpsons.add("Bart");
    simpsons.add("Margie");

    Iterator<String> simpsonsItr = simpsons.iterator();

    while (simpsonsItr.hasNext()) {
      String name = simpsonsItr.next();
      System.out.println(name);
      simpsonsItr.remove();
    }

    Assert.assertEquals(0, simpsons.size());
  }
}

Summary of actions:

  1. Created the Iterator class.
  2. Encapsulated an array inside the Iterator class.
  3. Implemented the Iterable interface in the Iterator class.
  4. Overrode the iterator method.
  5. Created an anonymous inner class of the Iterator type.
  6. Overrode all the methods from the Iterator interface.

To practice the Iterator Pattern you can create another Repository and iterate over the elements from it. Create another test method to make sure it works. Be sure to use TDD (Test Driven Development).

Written by
Rafael del Nero
Join the discussion

2 comments