Dataslope logoDataslope

Iterables and Iterators

The single contract — Iterable / Iterator — that unifies every container in the framework, and the for-each loop that rides on top of it

If you ask "what makes the Java Collections Framework feel coherent?" the deepest honest answer is: every container is iterable, and they are all iterable in the same way. Whether you have a List, a Set, a Map's entrySet, or your own custom container, the way you walk through it is the same. That uniformity is enforced by two tiny interfaces: Iterable<E> and Iterator<E>.

The two interfaces

That is it. Two interfaces, four methods.

  • Iterable<E> says "I can produce a fresh Iterator<E> on demand."
  • Iterator<E> says "I am a cursor — ask me if there are more elements, then ask me for the next one."

Every List, Set, Queue, and Deque in the JCF implements Iterable. Map does not directly (it isn't a Collection), but its keySet(), values(), and entrySet() views do.

The for-each loop is just sugar

Java's for-each loop (for (E x : iterable)) is compiled to a call on iterator() and a while over hasNext / next. The two fragments below produce identical bytecode:

Code Block
Java 8 (Update 492)

If you write your own class and implement Iterable<E>, the for-each loop just works on it. That is the return on implementing the interface.

Writing an Iterable from scratch

The fastest way to internalize the contract is to write one. Here is a tiny IntRange that produces the integers [start, end):

Code Block
Java 8 (Update 492)

Three observations:

  • The Iterator is created fresh by every call to iterator(). That is why you can iterate the same IntRange twice — each walk gets its own cursor.
  • next() is required to throw NoSuchElementException when called past the end. The contract is precise; library code relies on it.
  • The IntRange itself is immutable. The state of the walk lives in the iterator, not in the iterable. That separation is the whole trick.

Why a separate Iterator object?

A natural question: why doesn't the container itself carry the cursor? Two reasons, both fundamental.

1. Multiple walks at once. If the container held the cursor, you could not iterate it from two places at once.

Code Block
Java 8 (Update 492)

A list with a single cursor inside it could never produce that nested-loop output.

2. Streaming sources. Some "iterables" are not collections at all — they are generators. A live stream of events, a file's lines, a network response — each can implement Iterable and produce a cursor that pulls one element at a time. No need to materialize the whole sequence in memory.

Iterator.remove(): safe in-place deletion

The big functional advance over the old Enumeration is Iterator.remove(). It removes the element most recently returned by next() from the underlying collection, in O(1) for most implementations.

Code Block
Java 8 (Update 492)

Why does this matter? Because mutating a list with list.remove(i) inside a for-each loop is forbidden — it throws ConcurrentModificationException.

ConcurrentModificationException: the fail-fast guarantee

JCF iterators are fail-fast: if the underlying collection is modified by anyone other than the iterator itself during a walk, the next call to next() throws.

Code Block
Java 8 (Update 492)

This is a feature, not a bug. Detecting concurrent mutation early turns "mysterious data corruption" into "loud crash at the line of the mistake."

Iterating a Map

A Map is not a Collection, so it does not have its own iterator(). Instead it exposes three views, each of which is iterable:

Code Block
Java 8 (Update 492)

Prefer entrySet() when you need both: it walks the map once. Using keySet() and then m.get(k) walks it twice.

A multi-file example: an Iterable of an array

Let's wrap an int[] in an Iterable so it can be used in for-each. This is exactly how Arrays.asList and friends work conceptually.

Code Block
Java 8 (Update 492)

Practice

Challenge
Java 8 (Update 492)
Implement a CountdownIterable

Build a class Countdown that, when iterated, yields integers from start down to 1 inclusive.

  • Countdown takes one constructor argument: int start (assume start >= 1).
  • Countdown implements Iterable<Integer>.
  • Iterating a Countdown(3) should produce 3, 2, 1 in that order.
  • It must be safe to iterate the same Countdown twice — each walk should start over.

Expected output:

3
2
1
---
3
2
1

Test your understanding

QuestionSelect one

Why is the cursor state stored in the Iterator, not in the Iterable?

Because iterators are smaller in memory

So multiple independent walks of the same container can happen at once, each with its own cursor

Because Iterator is older than Iterable

Because the JVM forbids state in interfaces

QuestionSelect one

What exception do JCF iterators throw when the underlying collection is structurally modified during a walk (other than via the iterator itself)?

IllegalStateException

NoSuchElementException

ConcurrentModificationException

UnsupportedOperationException

QuestionSelect one

Which is the preferred way to iterate both keys and values of a Map exactly once?

Iterate keySet() and call get(key) for each

Iterate values()

Iterate entrySet() and read getKey() / getValue() on each entry

Convert the map to a List and iterate that

On this page