← thecodex.expert · The Codex Family of Knowledge
Kotlin

Standard Library Deep Dive

Chaining .map { }.filter { }.first { } on a List builds TWO full intermediate lists before finding the first match — the equivalent Sequence chain never builds either one.

Kotlin 2.1 Sequence is lazy, List is eager Last verified:
Canonical Definition

Calling .map { }.filter { } on a List is EAGER — map fully processes and allocates a complete new list before filter even starts on it. asSequence() converts a collection to a Sequence, where the same chain becomes LAZY: each element flows through map, then filter, then reaches the terminal operation one at a time, with no intermediate list ever fully materialized — genuinely faster and more memory-efficient specifically when a large collection is involved or when a terminal operation like first {} can stop early. takeIf returns the receiver if a predicate is true (or null otherwise); takeUnless is its exact inverse — both let a conditional check be chained inline rather than requiring a separate if statement.

List operations are eager: every step fully materializes

map runs across ALL one million elements, allocating a full new list, before filter even starts — wasteful when only the first matching result is actually needed.

Kotlineager_list.kt
val numbers = (1..1_000_000).toList()

val result = numbers
    .map { it * 2 }              // fully processes ALL 1,000,000 elements first
    .filter { it > 100 }          // THEN filters the entire resulting list
    .first()                        // only needed the FIRST one — but both steps already ran fully

Sequence: lazy, element-by-element

Each number flows through map, then filter, then reaches first() one at a time — processing stops the INSTANT a match is found, potentially touching only a handful of elements instead of a million.

Kotlinlazy_sequence.kt
val numbers = (1..1_000_000).asSequence()

val result = numbers
    .map { it * 2 }              // lazy — nothing runs yet
    .filter { it > 100 }          // still lazy
    .first()                        // NOW everything runs, element-by-element, stopping at the first match

takeIf and takeUnless: inline conditional checks

Both let a condition be woven directly into an expression chain, rather than requiring a separate if block that breaks the flow — genuinely convenient combined with a following ?.let or ?: for a fallback.

Kotlintake_if_unless.kt
val number = 15

val evenNumber = number.takeIf { it % 2 == 0 }   // null, since 15 is odd
println(evenNumber ?: "not even")                    // "not even"

val validated = number.takeUnless { it < 0 }        // 15, since it's NOT negative
println(validated)

Sources

1
JetBrains. Kotlin Documentation, "Sequences," kotlinlang.org/docs/sequences.html.