Iterators and Sequences
At closing time, the manager asks for "the first order today of more than 15 cups, after the promotion doubles them". There were thousands of orders. Doubling every single one to find the first match is wasteful. Iterators and sequences let Kotlin do only the work that is needed.
The rule
An iterator hands out one at a time; a sequence waits until asked.
Iterators. Every collection can give you an iterator, an object that walks through it:
val orders = listOf(2, 9, 4)
val iter = orders.iterator()
while (iter.hasNext()) {
println(iter.next()) // 2, then 9, then 4
}hasNext() asks "is there another?" and next() returns it and moves on. A for loop uses an iterator behind the scenes. A mutable collection's iterator can also remove() the current element, which is the safe way to delete while walking; removing from a list inside a plain for loop over it usually throws ConcurrentModificationException.
Sequences. Collection operations like map (change each element) and filter (keep some elements) are eager: each step processes the whole list and builds a new one. The braces hold a small function; it means "the current element".
Call asSequence() first and the chain becomes lazy: each element travels through all the steps, one at a time, and nothing happens until a terminal operation such as first(), toList() or count() asks for a result.
val orders = listOf(2, 9, 4, 12, 7)
val firstBig = orders.asSequence()
.map { it * 2 }
.first { it > 15 }
println(firstBig) // 18 — only 2 and 9 were doubledWithout asSequence(), all five orders are doubled first, and then the search starts.
Sequences can even be endless: generateSequence(1) { it * 2 }.take(5).toList() gives [1, 2, 4, 8, 16].
The shortcut
For a list chain, go step by step: finish each operation for every element before the next. For a sequence chain, go element by element: push one element all the way through, then the next, and stop when the terminal operation is satisfied.
Element 2
Doubled to 4. Is 4 > 15? No. Move on.
Sequences are not always faster: for small lists, the overhead of laziness can cost more than it saves. Reach for them on large data, long chains, or when you stop early.
Drills
Answer fast — trust the rule.
What does iter.next() do, where iter is an iterator?
listOf(1, 2, 3).asSequence().map { it * 10 } with no terminal operation. How many multiplications run?
In listOf(5, 20, 1).asSequence().filter { it > 3 }.first(), which elements are checked by the filter?
Which operation is terminal, making a sequence actually compute?
Remember
- An iterator hands out elements one at a time:
hasNext, thennext. - Lists run chains step by step; sequences run element by element, lazily.
- Nothing happens in a sequence until a terminal operation asks.
