Pebellslearn by building
0%
RoadmapsMatrixCoursesSandboxSign in
Collections, Ranges and Sequences  ›  Capstone Project

Iterators and Sequences

Iterators 15 minAn iterator hands out a collection's elements one at a time with hasNext and next, and a sequence uses that one-at-a-time style to run a chain of operations lazily, doing work only when a result is requested.

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 doubled

Without 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.

The sequence, element by elementStep 1 of 4
  1. 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.

quick drill

What does iter.next() do, where iter is an iterator?

quick drill

listOf(1, 2, 3).asSequence().map { it * 10 } with no terminal operation. How many multiplications run?

quick drill

In listOf(5, 20, 1).asSequence().filter { it > 3 }.first(), which elements are checked by the filter?

quick drill

Which operation is terminal, making a sequence actually compute?

Remember

  • An iterator hands out elements one at a time: hasNext, then next.
  • Lists run chains step by step; sequences run element by element, lazily.
  • Nothing happens in a sequence until a terminal operation asks.

Practice — level up

0/1 solved

Write your answer, then compare it with a rubric and a model answer.

ExplainFind the first big order, lazily

Orders are a list of cup counts: listOf(2, 9, 4, 12, 7). You want the first order that, after doubling, is greater than 15. Explain how many elements are doubled if you chain map and first directly on the list, compared with calling asSequence() first, and why.