Introduction
Iterator is a widely-used, simple, yet useful design pattern in both Java and .NET. In this article, I will focus on the Java version of the Iterator Pattern. Later, I will write another article for you about the .NET implementation of it.
Basically, the idea of the iterator pattern is to let the client code iterate over a collection. Usually, all the collections in Java have inbuilt support of iterator implementation. In Java, the iterator pattern implementation is straightforward.
Basic iterator implementation
In Java, we have the
Iterator interface (inside the java.util package). We can directly implement that interface into any collection where we need the iteration functionality.
In the Iterator, we have two abstract methods where we need to provide the implementation.
- interface Iterator {
- boolean hasNext();
- Object next();
- }
Have a look at the following code.
- public class SampleRepository implements Iterator {
- private String[] elements = {
- "A",
- "B",
- "C",
- "D"
- };
- private int position = 0;
- @Override
- public boolean hasNext() {
- return position < elements.length;
- }
- @Override
- public Object next() {
- if (this.hasNext())
- return elements[position++];
- return null;
- }
- }
Here, the SampleRepository class directly implements the Iterator Interface and allows to iterate over the list of elements inside the class.
Let’s see how we can iterate the collection.
- SampleRepository sampleRepository = new SampleRepository();
- while (sampleRepository.hasNext()) {
- System.out.println(sampleRepository.next());
- }
Since this is a string list, we can iterate the collection and it will provide the following output.
What happens if the list is not a string list anymore?
Suppose we have our own type called ‘Employee’.
- public class Employee {
- public int getId() {
- return id;
- }
- public void setId(int id) {
- this.id = id;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- private int id;
- private String name;
- public Employee(int id, String name) {
- this.id = id;
- this.name = name;
- }
- }
- private Employee[] elements = {
- new Employee(1, "A-Employee"),
- new Employee(2, "B-Employee")
- };
Then, what will happen when we print the values? We will get a similar output of the following kind.
That is because once we print the collection, it prints the addresses of the employee references. Unless we do the casting, there’s no way to access the properties of the employee. So, we have to do the casting like this and print the names of the employees
- while (sampleRepository.hasNext()) {
- System.out.println(((Employee) sampleRepository.next()).getName());
- }
What is the solution?
Generic iterator implementation
What if we build an iterator that provides a generic way of iterating over a collection independent of its type?
- public class SampleRepositoryGeneric < T > implements Iterator {
- private Employee[] elements = {
- new Employee(1, "A-Employee"),
- new Employee(2, "B-Employee")
- };
- private int position = 0;
- @Override
- public boolean hasNext() {
- return position < elements.length;
- }
- @Override
- public T next() {
- if (this.hasNext())
- return (T) elements[position++];
- return null;
- }
- }
Here, we have made the sample repository to use <T>. And we have an easier and more efficient implementation once we call the collection for iteration. Actually, no casting!
- SampleRepositoryGeneric < Employee > sampleRepositoryGeneric = new SampleRepositoryGeneric < > ();
- while (sampleRepositoryGeneric.hasNext()) {
- System.out.println(sampleRepositoryGeneric.next().getName());
- }
Further refactoring
There’s one more thing we could do to clean the code.
- public class SampleRepository < T > {
- private Employee[] elements = {
- new Employee(1, "A-Employee-Refactored"),
- new Employee(2, "B-Employee-Refactored")
- };private int position = 0;public Iterator < T > getIterator() {
- return new SampleRepositoryIterator();
- }
- private class SampleRepositoryIterator implements Iterator {
- @Override
- public boolean hasNext() {
- return position < elements.length;
- }
- @Override
- public T next() {
- if (this.hasNext())
- return (T) elements[position++];
- return null;
- }
- }
- }
Basically, what we have done is that we have made the iterator an internal class and exposed the iterator as a property to the outside users. So, we have encapsulated the iterator, and we can call the iterator like this.
- refactored.iterator.SampleRepository < Employee > sampleRepository1 = new refactored.iterator.SampleRepository < > ();
- while (sampleRepository1.getIterator().hasNext()) {
- System.out.println(sampleRepository1.getIterator().next().getName());
- }
That's it for this article. Cheers!!