Behavioral · Gang of Four
Iterator
Iterator is a behavioral pattern that walks a collection without exposing how it is stored. Clients ask for the next element; they do not dig into floors and lists by hand.
Example: a parking lot has floors, and each floor has spots. A report wants every free EV spot. If the report writes lot.getFloors().get(i).getSpots(), it owns the shape of the lot. An iterator (or a stream from the lot) hides that shape.
One walk
The cursor, not the list
Iterator is a . The creates it. The caller never sees Floor. That is encapsulation, not a clever data structure. Collections-ownership says the lot owns the spots. Iterator is how you let people look without lending them the list.
- 1EV-1next()
- 2EV-2next()
- 3STD-3next()
- 4STD-4next()
- 5STD-5next()
- 6EV-6next()
Returning the internal list
getSpots() that returns the live ArrayList is not an iterator. Callers will sort it, remove from it, and store it. Either copy, wrap as unmodifiable, or hand out an iterator. See encapsulation.
Leaking Node
An iterator whose next() returns a linked-list Node with .prev and .next has not hidden structure. Next should be the domain object: ParkingSpot, File, Cell.
Java: spots without exposing floors
Implement so for-each works. The anonymous iterator holds two indices. A parking lot report can filter EV spots without knowing Floor exists.
interface SpotIterator {
boolean hasNext();
ParkingSpot next();
}
class LotSpots implements Iterable<ParkingSpot> {
private final List<Floor> floors;
public Iterator<ParkingSpot> iterator() {
return new Iterator<>() {
int f = 0, s = 0;
public boolean hasNext() {
while (f < floors.size()) {
if (s < floors.get(f).spotCount()) return true;
f++;
s = 0;
}
return false;
}
public ParkingSpot next() {
if (!hasNext()) throw new NoSuchElementException();
return floors.get(f).spot(s++);
}
};
}
}
// for (ParkingSpot spot : lot.spots()) { ... }When the walk is not a List
Floors of spots
Flatten in the iterator. The lot can skip a closed floor without the report changing.
File tree
A Composite can offer depth-first and breadth-first iterators without exposing Node.
Occupied squares
A chess board can iterate occupied squares. Callers never see the 8×8 array.
One iterator, one walk. Do not mutate the collection under the iterator unless you have a story. Java’s ConcurrentModificationException is that story.
Live list, copy, or iterator?
Three doors, three failure modes. Pick one and say why. Returning the live ArrayList is the default bug in parking-lot rounds.
| Door | Caller can | Use when |
|---|---|---|
| Live list | clear(), sort, stash a reference | Almost never in LLD |
| Unmodifiable copy | Read a snapshot, not mutate yours | Small collections, random access |
| Iterator / Iterable | Walk once; no structure | Nested floors, trees, huge walks |
Java: skip closed floors without leaking Floor
hasNext advances both indices. A closed floor is a continue, not a type the report must know.
public boolean hasNext() {
while (f < floors.size()) {
Floor floor = floors.get(f);
if (floor.isClosed()) { f++; s = 0; continue; }
if (s < floor.spotCount()) return true;
f++;
s = 0;
}
return false;
}Fail-fast is a feature
If park() adds a spot while a report iterates, throw. Silent skip is worse. Snapshot the free list if the report must be stable.
Two iterators, two algorithms
Depth-first and breadth-first on a file tree are two iterator types. The Node type still stays private.
How to explain this in an interview
“The lot owns floors. The report walks spots. If a floor closes, the iterator skips it and the report does not change.” That rule is the pattern. getSpots() returning ArrayList is the anti-pattern. One iterator, one walk; mutating under it should fail fast.
Nearby in LLD