skip to content

Iterable & Iterator Protocol

Implementing Iterable — or just an operator iterator() — is what makes your own type usable in a for-in loop. The mutable variant with remove(), and ListIterator's bidirectional cursor, are the follow-up details.

part ofKotlinoverview, primer and where to startread it →
on this pageshow

questions

5

What does Kotlin's `for (x in something)` loop actually require of `something`, and how does it relate to `Iterable` and `Iterator`?

level: juniorimportance: must knowfreq 70%

answer

  1. for-in = sugar for iterator()/hasNext()/next()
  2. operator funs, can be extensions
  3. iterator() called once per loop
  4. Iterable declares iterator(), Iterator declares hasNext/next
  5. Collection : Iterable

basics

~10 s

A for loop works on anything that has an iterator() function. That function returns an iterator with hasNext() and next(), which the loop calls repeatedly to walk through the elements.

solid answer

~40 s

Kotlin's `for (x in c)` is syntactic sugar. The compiler requires `c` to provide an `operator fun iterator()` returning an object with `operator fun hasNext(): Boolean` and `operator fun next(): T`. `Iterable<T>` is the standard interface declaring `iterator(): Iterator<T>`, and `Iterator<T>` declares `hasNext()`/`next()`. But you don't strictly need to implement `Iterable`; any type with those `operator` functions (even via extension functions) becomes loop-able. The loop desugars to: create the iterator once, then `while (it.hasNext()) { val x = it.next(); ... }`. Arrays, ranges (`IntRange`), strings, and `Sequence` are iterable through this same protocol. This convention-based design is why `0..10`, a `CharSequence`, and a custom class can all share one loop syntax.

code

kotlin · 9 lines
kotlin
class Countdown(val from: Int) {
    operator fun iterator() = object : Iterator<Int> {
        var current = from
        override fun hasNext() = current >= 0
        override fun next() = current--
    }
}

for (n in Countdown(3)) print("$n ") // 3 2 1 0

go deeper

for a junior

Knows for walks elements and that there's an iterator() with hasNext()/next().

for a middle

Can describe the exact desugaring and that the functions are operator and can be extensions.

for a senior

Explains the convention-vs-interface distinction, why Iterable unlocks stdlib operators, and iterator state/lifetime.

for a principal

Discusses designing APIs around the protocol, variance (out T), and trade-offs of operator-convention vs. nominal Iterable for library ergonomics.

## The `for` loop is a convention, not a hard interface Kotlin has no built-in `for`-each tied to a single type. Instead, `for (x in c) { ... }` is **desugared by the compiler** into iterator calls. For this to compile, `c` must expose three `operator` functions: - `operator fun iterator(): I` — returns an iterator object. - `I.operator fun hasNext(): Boolean` — is there another element? - `I.operator fun next(): T` — return the next element and advance. These can be **members** or **extension functions**. That's why you can make a third-party type loop-able by writing an extension `operator fun Foo.iterator()`. ## The standard interfaces The stdlib formalizes the common case: ```kotlin public interface Iterable<out T> { public operator fun iterator(): Iterator<T> } public interface Iterator<out T> { public operator fun next(): T public operator fun hasNext(): Boolean } ``` `Collection<T>` (and thus `List`, `Set`) extends `Iterable<T>`. So every collection is loop-able for free. ## How the loop desugars ```kotlin for (x in c) { use(x) } // becomes roughly: val it = c.iterator() while (it.hasNext()) { val x = it.next() use(x) } ``` Key consequences: - `iterator()` is called **once** per loop — the same iterator drives the whole loop. - `next()` should be called only after `hasNext()` returns `true`; otherwise the iterator typically throws `NoSuchElementException`. - The iterator holds the **traversal state** (cursor position), so the source collection stays stateless across iterations. ## Why this matters Because it's convention-based, `IntRange` (`0..10`), `CharSequence`, arrays, maps' entry sets, and your own classes can all be looped with identical syntax. You implement `Iterable<T>` when you want your type to be a first-class collection-like source and to inherit all the stdlib extension operators (`map`, `filter`, `forEach`, etc.) that are defined on `Iterable<T>`.

  • Do you have to implement `Iterable` to use it in a `for` loop?
    No. You only need `operator fun iterator()` returning an object with `operator hasNext()`/`next()`. These can be extension functions, so even a type you don't own can be made loop-able.
  • What free functionality do you gain by implementing `Iterable<T>` instead of just `operator iterator()`?
    All stdlib extensions on `Iterable<T>` — `map`, `filter`, `forEach`, `sumOf`, `groupBy`, etc. — plus interoperability anywhere an `Iterable` is expected.

An iterator is like a bookmark that walks a book page by page: hasNext() checks for another page, next() reads it and moves the bookmark forward.

saying these in an interview costs you the question

  • Claiming `for` requires implementing `Iterable` (the operator convention is enough)
  • Saying `iterator()` is called every loop pass rather than once
  • Calling `next()` without `hasNext()` and not knowing it throws `NoSuchElementException`
  • Confusing `hasNext()` with consuming the element (it only peeks/checks)
  • Thinking `for` works on `Sequence` differently — it uses the same protocol

context

open as a page

Write a custom type that supports `for`-in by implementing the iterator protocol, and explain where the traversal state lives.

level: middleimportance: should knowfreq 55%

basics

~20 s

Add an operator fun iterator() that returns an object tracking a position. That object's hasNext() checks if more items remain and next() returns the current item and moves forward. The position lives in the iterator, not the collection.

open as a page

How does `MutableIterator.remove()` work, and why is it the safe way to delete elements while iterating a `MutableList`?

level: middleimportance: should knowfreq 45%

basics

~10 s

MutableIterator adds a remove() method that deletes the element you just returned with next(). Using it keeps the iterator consistent, so you can remove items during a loop without corrupting the traversal.

open as a page

An `Iterator` is single-use while an `Iterable` can be traversed repeatedly. How does this distinction affect API design and the relationship between `Iterable` and `Sequence`?

level: seniorimportance: should knowfreq 35%

basics

~20 s

An Iterator is consumed once — after you walk it, it's exhausted. An Iterable can hand out a fresh iterator each time, so you can loop over it again and again. So expose Iterable (re-iterable) in APIs, not a bare Iterator.

open as a page

What does `ListIterator` add over a plain `Iterator`, and when would you reach for it?

level: seniorimportance: nice to knowfreq 30%

basics

~10 s

ListIterator lets you walk a list both forward and backward. It adds previous(), hasPrevious(), and index queries nextIndex()/previousIndex(). The mutable version can also replace and insert elements during the walk.

open as a page