In this design pattern tutorial series we will discuss about iterator design pattern, which allows to traverse a collection without exposing its internals. Ah we all know about the Java Iterator. But it is not all about it, lets dive deeper and do a comprehensive study of iterator pattern. By the end of this tutorial we will know about internal iterator, external iterator, polymorphic iterator, robust iterator, null iterator and etc.
Intent of an iterator is to provide a way to traverse through a collection (otherwise called an aggregate) in a sequential way without exposing the underlying collection. Initially Java had Enumeration to iterate through the collections and from Java version 1.2 Iterator took its place. Java Iterator is a nice implementation of iterator design pattern allowing us to traverse through a collection in a sequential way. On call, the collection will create an iterator instance and return it, which we can use to iterate.
In the Iterator design pattern, iterator is an interface which provides a simple interaction mechanism. More importantly the user of the iterator interface need not know about the working mechanisms or complexity of the related collection. All, the client needs to know is about the simple interface provided by the iterator.
Iterators are not limited to traversal alone. It is entirely left to the purpose and implementation. There can be functional logic involved in an iterator. We can have a FilteringIterator, which can filter out certain required values and provide for traversal. For example in a list containing wild and domestic animals, we can have two different iterators as WildAnimalIterator and DomesticAnimalIterator.
A factory method available in the collection will return the instance of the iterator.
If an iterator has common contract across multiple collections, the client code which interacts with the iterator need not be changed for different collections. This is called polymorphic iterator.
When the client controls the iteration sequence and index position then it is called as the external iterator. Otherwise if the iterator controls the traversal then it is called internal iterator. On external iterator, the design is that the invoking client code should explicitly invoke the method to move the pointer to next element. For internal iterator, when an element is accessed, after access the pointer will be automatically moved to next index by the iterator. In general internal iterators are easier to use than the external iterators.
This is an important topic. Imagine an iterator instance is at work and what would happen if the contents of the underlying collection is modified like a new element is inserted or an existing element is deleted? It will behave inconsistently resulting in erroneous output. A simplistic iterator will create a copy of the collection when it is instantiated and use that for traversal. So that during the iteration process, even if the collection is modified nothing would happen.
A robust iterator is the one that works intact even if the associated collection is modified. It even does the implementation without making a copy of the association. That is the effects of modification of the collection will also be absorbed into the iterator. For example, it can be implemented using an event listener notifying the iterator of the changes and it gets updated for the changes.
Imagine we are traversing a tree data structure. During the tree traversal, when we ask for the nextElement, we will get a concrete iterator which will help us to traverse through the tree. If we ask for the nextElement in the leaf node, we will get a null iterator returned by the collection signifying the leaf node. This behavior will allow us to design the tree traversal pattern.
package com.javapapers.designpattern.iterator; public class Animal { private String animalName; private String animalType; public Animal(String animalName, String animalType) { this.animalName = animalName; this.animalType = animalType; } public String getAnimalName() { return animalName; } public void setAnimalName(String animalName) { this.animalName = animalName; } public String getAnimalType() { return animalType; } public void setAnimalType(String animalType) { this.animalType = animalType; } }
package com.javapapers.designpattern.iterator; import java.util.List; public interface IZoo { public ListgetAnimals(); public void addAnimal(Animal animal); public void removeAnimal(Animal animal); public Iterator createIterator(String iteratorType); }
package com.javapapers.designpattern.iterator; import java.util.ArrayList; import java.util.List; public class ZooImpl implements IZoo { ListanimalList; public ZooImpl() { animalList = new ArrayList (); } @Override public List getAnimals() { return animalList; } @Override public void addAnimal(Animal animal) { animalList.add(animal); } @Override public void removeAnimal(Animal animal) { animalList.remove(animal); } @Override public Iterator createIterator(String iteratorType) { if ("Wild".equals(iteratorType)) { return new WildIterator(animalList); } else { return new DomesticIterator(animalList); } } }
package com.javapapers.designpattern.iterator; public interface Iterator { public Animal nextAnimal(); public boolean isLastAnimal(); public Animal currentAnimal(); }
package com.javapapers.designpattern.iterator; import java.util.List; public class WildIterator implements Iterator { public ListanimalList; private int position; public WildIterator(List animalList) { this.animalList = animalList; } @Override public Animal nextAnimal() { Animal animal = null; for (; position < animalList.size(); position++) { if ("Wild".equals((animalList.get(position)).getAnimalType())) { animal = animalList.get(position); position++; break; } } return animal; } @Override public boolean isLastAnimal() { for (int i = position; i < animalList.size(); i++) { if ("Wild".equals((animalList.get(i)).getAnimalType())) { return false; } } return true; } @Override public Animal currentAnimal() { if (position < animalList.size()) { return animalList.get(position); } return null; } }
package com.javapapers.designpattern.iterator; import java.util.List; public class DomesticIterator implements Iterator { ListanimalList; private int position; public DomesticIterator(List animalList) { this.animalList = animalList; } @Override public Animal nextAnimal() { Animal animal = null; for (; position < animalList.size(); position++) { if ("Domestic".equals((animalList.get(position)).getAnimalType())) { animal = animalList.get(position); position++; break; } } return animal; } @Override public boolean isLastAnimal() { for (int i = position; i < animalList.size(); i++) { if ("Domestic".equals((animalList.get(i)).getAnimalType())) { return false; } } return true; } @Override public Animal currentAnimal() { if (position < animalList.size()) { return animalList.get(position); } return null; } }
package com.javapapers.designpattern.iterator; public class IteratorSample { public static void main(String args[]) { ZooImpl zoo = new ZooImpl(); zoo.addAnimal(new Animal("Tiger", "Wild")); zoo.addAnimal(new Animal("Lion", "Wild")); zoo.addAnimal(new Animal("Tom Cat", "Domestic")); zoo.addAnimal(new Animal("Raging Bull", "Wild")); zoo.addAnimal(new Animal("Scooby Doo", "Domestic")); Iterator wildIterator = zoo.createIterator("Wild"); while (!wildIterator.isLastAnimal()) { System.out.println("Wild Animal: " + wildIterator.nextAnimal().getAnimalName()); } Iterator domesticIterator = zoo.createIterator("Domestic"); while (!domesticIterator.isLastAnimal()) { System.out.println("Domestic Animal: " + domesticIterator.nextAnimal().getAnimalName()); } } }
Wild Animal: Tiger
Wild Animal: Lion
Wild Animal: Raging Bull
Domestic Animal: Tom Cat
Domestic Animal: Scooby Doo
Iterator Design Pattern Source Code
Comments are closed for "Iterator Design Pattern".
Very nice post!!
well done!
the content is definitely worth it… but what is that police wala doing in tech stuff? :D
Ah! he is the traffic Iterator, sending one by one in sequence. Fun isn’t it :D
Very useful this post..thanks
We usually don’t use iterators for the company I work for. Most of our managers says it has slow performance.
I am appreciating your hard work, You are doing very good job, I am bringing
my observation to your notice
public class ZooImpl implements IZoo {
List animalList;
@Override
public List getAnimals() {
return animalList;
}
In the ZooImpl, animalList is our collection, which stores all our animals,
While calling getAnimals(), you are returning the animalList storage collection
to client.So that cient may modify collection like
animalList.add(animal) or animalList.remove(animal).
As the iterator pattern, “provides a way to access the elements of an aggregate
Object sequentially without exposing its underlying representation”
In this example, you are exposing the underlying representation of animalList.
Well explained :)
As always, neat and good explanation !
Nice explanation.
pretty code
I appreciate your hard-work. It is really very good and step by step explanation with example.
Hi.. Useful explained in a simple n clear way..
Keep going .. :)
It seems that `position` is not initialized in WildIterator&DemosticIterator