skip to content

How would you make your own class iterable with the for-each loop, and what is the contract you must satisfy?

level: seniorimportance: nice to knowfreq 45%

answer

  1. implement Iterable<T> -> iterator()
  2. iterator implements hasNext()/next() (+ optional remove)
  3. fresh iterator per iterator() call
  4. next() past end throws NoSuchElementException
  5. for-each is sugar over iterator()/hasNext()/next()

basics

~10 s

Implement the Iterable<T> interface and provide an iterator() method that returns an Iterator<T>. The iterator needs hasNext() and next() (and optionally remove()). Then your object works in a for-each loop.

solid answer

~40 s

To support for-each, your class implements Iterable<T>, whose single method iterator() returns a fresh Iterator<T> each time. The returned iterator must implement hasNext() (true while elements remain) and next() (return the next element, advance, and throw NoSuchElementException past the end); remove() is optional and should throw UnsupportedOperationException if you don't support it. Each call to iterator() should return an independent cursor so multiple concurrent iterations don't interfere. The compiler rewrites for (T t : obj) into iterator()/hasNext()/next() calls, so satisfying that contract is all you need. A common implementation is a private inner class holding an index or node pointer over your backing data. If you want fail-fast behavior, track a modCount and check it in next(). For Java 8+ you also inherit forEachRemaining and can override spliterator() if you want stream support.

code

java · 18 lines
java
public final class Range implements Iterable<Integer> {
    private final int start, endExclusive;
    public Range(int start, int endExclusive) {
        this.start = start;
        this.endExclusive = endExclusive;
    }
    @Override public Iterator<Integer> iterator() {
        return new Iterator<>() {                 // fresh cursor each call
            private int cur = start;
            @Override public boolean hasNext() { return cur < endExclusive; }
            @Override public Integer next() {
                if (!hasNext()) throw new NoSuchElementException();
                return cur++;
            }
        };
    }
}
// for (int i : new Range(0, 3)) { ... }  // 0, 1, 2

go deeper

for a junior

Know that implementing Iterable with an iterator() method makes a class usable in a for-each loop.

for a middle

Write a working Iterable with an inner Iterator implementing hasNext()/next(), throwing NoSuchElementException at the end, and explain the for-each desugaring.

for a senior

Articulate the full contract: fresh independent iterators, side-effect-free hasNext(), optional remove() semantics, and how to add fail-fast behavior.

for a principal

Decide iteration semantics (reusable vs single-use), provide an efficient spliterator() for parallel streams, and design the iterator's exception/optional-operation contract for library consumers.

## Goal You want your own type — say a `Range` or a ring buffer — to work in `for (X x : myObject)`. The for-each loop is defined by the compiler to operate on any **`Iterable`**, so that is the interface to implement. ## The two interfaces - **`Iterable<T>`** has exactly one abstract method: `Iterator<T> iterator()`. Implement it on your container class. - **`Iterator<T>`** is the cursor: `boolean hasNext()`, `T next()`, and the optional `default void remove()`. ## Minimal correct implementation ```java public final class Range implements Iterable<Integer> { private final int start, endExclusive; public Range(int start, int endExclusive) { this.start = start; this.endExclusive = endExclusive; } @Override public Iterator<Integer> iterator() { return new Iterator<>() { // fresh cursor each call private int cur = start; @Override public boolean hasNext() { return cur < endExclusive; } @Override public Integer next() { if (!hasNext()) throw new NoSuchElementException(); return cur++; } }; } } // usage: for (int i : new Range(0, 3)) { ... } // 0,1,2 ``` ## The contract you must honor 1. **`iterator()` returns a fresh, independent iterator every call.** Returning a shared/stateful singleton breaks nested or repeated loops. If your object is meant to be iterated once (like a stream), document that, but the collections convention is reusable. 2. **`hasNext()` is idempotent and side-effect-free.** It must not consume an element; callers may call it multiple times before `next()`. 3. **`next()` advances exactly one position and returns the element; past the end it throws `NoSuchElementException`.** Don't return `null` as an end sentinel. 4. **`remove()` is optional.** If unsupported, the inherited default already throws `UnsupportedOperationException`, so you can simply not implement it. If you do support it, it must remove the element the last `next()` returned and obey the `IllegalStateException` rule. 5. **Type alignment.** `Iterable<T>` and the returned `Iterator<T>` must use the same element type `T`. ## How for-each uses it The compiler rewrites: ```java for (Integer i : range) { use(i); } ``` into roughly: ```java Iterator<Integer> it = range.iterator(); while (it.hasNext()) { Integer i = it.next(); use(i); } ``` So if your `iterator()`/`hasNext()`/`next()` are correct, for-each just works — no extra wiring. ## Optional refinements - **Fail-fast**: keep an `int modCount` on the container, snapshot it in the iterator, and throw `ConcurrentModificationException` from `next()` if it changed — matching java.util semantics. - **`forEachRemaining(Consumer)`**: inherited default; override only if you can drain more efficiently. - **`spliterator()` / streams**: `Iterable` provides a default `spliterator()` built from your iterator, so `StreamSupport.stream(range.spliterator(), false)` works; override `spliterator()` for better splitting/parallelism. - **Inner vs static nested class**: a non-static inner class can read the enclosing instance's fields directly; a static nested class needs them passed in. Either is fine. ## Term recap - **Iterable**: the thing you can loop over; supplies iterators. - **Iterator**: the single-use cursor an Iterable produces. - **Idempotent**: calling repeatedly has the same effect as once (true of `hasNext`). - **Sentinel**: a special end-marker value; avoid using `null` as one — throw instead. - **Spliterator**: a splittable iterator that backs streams and enables parallel traversal.

  • What single method does Iterable require, and what does it return?
    iterator(), which returns a new Iterator<T> over the elements.
  • Why should iterator() return a new iterator each time?
    So independent traversals — nested loops, re-iteration, concurrent reads — don't share and corrupt each other's cursor state.

saying these in an interview costs you the question

  • Returning the same stateful iterator from every iterator() call (breaks nested/repeated loops)
  • Using null from next() as an end-of-data sentinel instead of throwing
  • Making hasNext() consume an element (it must be side-effect-free)
  • Confusing which interface for-each requires (it's Iterable, not Iterator)

context