Card 02/ 08

ExampleDifficulty: Beginner2 min

Design Patterns Example: Iterator in Three Languages

A Python for loop, a Java for-each and a TypeScript for...of are one solution under three syntaxes.

A pattern is called recurring because the same solution appears wherever the same problem does, and the Iterator pattern is the easiest place to see that. Every time you loop over a list, an iterator object does the walking. This example shows the same loop in Python, Java and TypeScript, first as the loop you normally write and then with the iterator made visible.

In each listing the first loop is the everyday form. The lines after it ask the collection for its iterator and call it directly, which is what the loop does for you.

The loop, then the iterator behind it

py
words = ["adapter", "bridge", "composite"]

for word in words:
    print(word)

iterator = iter(words)
print(next(iterator))
print(next(iterator))
print(next(iterator))
try:
    next(iterator)
except StopIteration:
    print("StopIteration: nothing left")
OutputRan with python:3.12-slim (Python 3.12.13)
adapter
bridge
composite
adapter
bridge
composite
StopIteration: nothing left
`iter()` asks the list for an iterator, and each `next()` takes one element.
java
import java.util.Iterator;
import java.util.List;

public class WdIterate {
    public static void main(String[] args) {
        List<String> words = List.of("adapter", "bridge", "composite");

        for (String word : words) {
            System.out.println(word);
        }

        Iterator<String> iterator = words.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
        System.out.println("hasNext: " + iterator.hasNext());
    }
}
OutputRan with eclipse-temurin:21-jdk (Java 21.0.12)
adapter
bridge
composite
adapter
bridge
composite
hasNext: false
`iterator()` returns the object the for-each loop uses, with `hasNext()` and `next()`.
ts
const words: string[] = ["adapter", "bridge", "composite"];

for (const word of words) {
  console.log(word);
}

const iterator = words[Symbol.iterator]();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
OutputRan with node:22-slim (Node 22.23.1), TypeScript 5.9.3
adapter
bridge
composite
{ value: 'adapter', done: false }
{ value: 'bridge', done: false }
{ value: 'composite', done: false }
{ value: undefined, done: true }
`Symbol.iterator` returns the object `for...of` uses; each `next()` reports a value and whether it is done.

The three languages spell it differently, and the shape underneath is the same. The collection creates an iterator. The iterator remembers where it is. Each call hands out the next element, and the iterator says when there are none left.

The same two roles in three languages
Role in the patternPythonJavaTypeScript
The collection creates an iteratoriter(words)words.iterator()words[Symbol.iterator]()
The iterator hands out the next elementnext(iterator)iterator.next()iterator.next().value
The iterator reports the endraises StopIterationhasNext() returns falsenext() returns done: true

None of the three loops mentions how a list is stored. That is the problem the pattern solves, and it is why the same loop works over a set, a dictionary's keys or a file's lines: each supplies its own iterator, and the loop only ever calls next.

This is what "recurring" means. Three language designers, years apart, met the same problem and reached the same two roles. A pattern catalogue names those roles once, so you can recognise them in a language you have never used.