The Iterator pattern separates the way we visit elements from the way those elements are stored. A caller asks whether another element is available and then requests it, without managing array positions or links between nodes.
Use Iterator when callers need to traverse a collection through a consistent API while the collection keeps control of its storage and traversal order. Our example stores the three most recently viewed items in a circular array and visits them from newest to oldest.
1. How the Iterator Pattern Works
Java’s Iterable<T> describes an object that can supply an iterator. Its iterator() method returns an Iterator<T>, which keeps the current traversal position.
The iterator’s hasNext() reports whether another element is available. Its next() returns that element and advances. Calling next() after exhaustion must throw NoSuchElementException. Removal is optional; the default remove() implementation throws UnsupportedOperationException. These rules come from Java’s Iterator API.
An enhanced for loop uses these operations for us. Our collection creates a fresh iterator for every call, allowing separate traversals to have separate positions. We should not assume every API implementing Iterable permits unlimited or repeatable iteration.
The roles map to the example as follows.
| Participant | Responsibility |
|---|---|
| Aggregate | RecentlyViewed stores items and implements Iterable. |
| Iterator contract | Iterator defines checking and retrieving the next element. |
| Concrete iterator | RecentIterator holds one traversal position. |
| Client | An enhanced loop or calling code consumes the iterator. |
The collection supplies an iterator with its own position. Moving that iterator does not expose the array or move another iterator.
2. Store Recent Items in a Circular Array
nextIndex is the array position where the next addition will be stored. Once the array fills, a new item replaces the oldest one. size counts the retained items. modCount is a change counter: each addition increases it, so an iterator can notice that the collection changed after its creation.
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Objects;
final class RecentlyViewed implements Iterable<String> {
private final String[] items;
private int nextIndex;
private int size;
private int modCount;
RecentlyViewed(int capacity) {
if (capacity < 1) {
throw new IllegalArgumentException("Capacity must be positive");
}
items = new String[capacity];
}
void add(String item) {
items[nextIndex] = Objects.requireNonNull(item);
nextIndex = (nextIndex + 1) % items.length;
size = Math.min(size + 1, items.length);
modCount++;
}
public Iterator<String> iterator() {
return new RecentIterator(true);
}
Iterable<String> oldestFirst() {
return () -> new RecentIterator(false);
}
private final class RecentIterator implements Iterator<String> {
private final boolean newestFirst;
private final int expectedModCount = modCount;
private int returned;
RecentIterator(boolean newestFirst) {
this.newestFirst = newestFirst;
}
public boolean hasNext() {
checkForChanges();
return returned < size;
}
public String next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
int index = newestFirst
? Math.floorMod(nextIndex - 1 - returned, items.length)
: Math.floorMod(nextIndex - size + returned, items.length);
returned++;
return items[index];
}
private void checkForChanges() {
if (expectedModCount != modCount) {
throw new ConcurrentModificationException();
}
}
}
}
The inner iterator can read the enclosing collection’s fields. It keeps its own returned count, so creating a second iterator does not move the first one. Math.floorMod() converts a negative calculated position into a valid array index when traversal wraps around the array.
oldestFirst() returns an Iterable whose iterators walk in the opposite direction. It reads the same collection; it does not copy or freeze its contents.
3. Run Both Traversal Orders
Place the imports and RecentlyViewed above this entry point in IteratorDemo.java.
public class IteratorDemo {
public static void main(String[] args) {
RecentlyViewed recent = new RecentlyViewed(3);
recent.add("Apple");
recent.add("Banana");
recent.add("Cherry");
recent.add("Mango");
for (String item : recent) {
System.out.println(item);
}
System.out.println("---");
for (String item : recent.oldestFirst()) {
System.out.println(item);
}
}
}
javac IteratorDemo.java
java IteratorDemo
Mango
Cherry
Banana
---
Banana
Cherry
Mango
Adding Mango replaces Apple because capacity is three. The first loop visits the retained items from newest to oldest. The second loop visits the same items in the opposite order, without knowing their physical array positions.
4. Define What Happens When the Collection Changes
Our iterator records modCount when it is created. A later addition changes that value, and the next iterator check throws ConcurrentModificationException. This is our chosen fail-fast policy, which reports an unexpected structural change rather than continuing with a changed traversal. Oracle describes this exception as a bug detector, not a correctness guarantee.
Fail-fast detection is not a thread-safety mechanism or a requirement for every iterator. This collection is not thread-safe. Concurrent collections can use other policies, including weakly consistent traversal or snapshot iteration. Java’s Iterator contract leaves modification behavior to the implementation unless an operation has a specific guarantee.
For a stable view while additions continue, we could copy the retained elements and iterate over that snapshot. That costs extra memory but gives a different, useful contract. The Observer pattern uses a copied listener list for a related reason.
5. When a Custom Iterator Is Useful
Use the existing collection iterator when it already provides the required order and behavior. A custom iterator earns its place when a data structure needs a specific traversal, hides an unusual representation or produces elements lazily.
The common API does not promise equal performance across data structures. Each implementation determines how much work hasNext() and next() perform. Resource-backed iterators may also need an explicit closeable API; ordinary Iterator has no close() method.
The Composite pattern represents nested objects. An iterator can separately define how callers visit that tree, for example depth-first or breadth-first.
Browse the complete Iterator example on GitHub to compile and run it locally.
6. Conclusion
Iterator gives callers a consistent traversal operation while the collection owns storage and ordering. Our circular collection exposes two orders without exposing its array. Exhaustion, modification, concurrency and resource behavior remain part of the implementation’s contract.
Iterator pattern de-couple the algorithm logic from the data structure. Suppose there is a method which calculates the sum of all numbers provided by an iterator object. It doesn’t matter whether numbers are stored in linked list or binary tree as long as Iterator objects provides a way to traverse all numbers, the method can calculate the sum.
Here can i know where did u used topiciterator
Its used in Main method above while loop