๐ŸŽ“ A1Learner
๐Ÿ”
0 of 37 lessons completed

Kotlin Sequences

A sequence processes values one at a time instead of in separate passes over the whole collection. Work starts only when a final step asks for values.

Eager lists and lazy sequences side by side

The list finishes every map call before any filter call. The sequence sends each value through map and then filter before moving on.

fun main() {
    val nums = listOf(1, 2, 3, 4)
    val eager = nums.map {
        println("eager map $it")
        it * 10
    }.filter {
        println("eager filter $it")
        it > 15
    }
    println(eager)
    val lazy = nums.asSequence().map {
        println("seq map $it")
        it * 10
    }.filter {
        println("seq filter $it")
        it > 15
    }.toList()
    println(lazy)
}

Stopping early with first

first asks for one matching value only. The sequence stops after the second value and never touches the rest.

fun main() {
    val nums = listOf(1, 2, 3, 4)
    val first = nums.asSequence().map {
        println("seq map $it")
        it * 10
    }.filter {
        println("seq filter $it")
        it > 15
    }.first()
    println(first)
}

No work before a final step

Building the sequence prints nothing at first. The toList call starts the work, so the built line comes first.

fun main() {
    val nums = listOf(1, 2, 3)
    val seq = nums.asSequence().map {
        println("mapping $it")
        it + 1
    }
    println("built")
    println(seq.toList())
}

Generating values with take

generateSequence starts at 1 and doubles each value to make the next one. take keeps only the first five values.

fun main() {
    val powers = generateSequence(1) { it * 2 }
    println(powers.take(5).toList())
}

Example

fun main() {
    val words = listOf("ant", "ape", "bat", "bee")
    val result = words.asSequence().filter {
        println("checking $it")
        it.startsWith("b")
    }.first()
    println(result)
}

Key points

  • A list pipeline finishes one whole step before the next step starts.
  • A sequence pipeline moves each value through all the steps before the next value.
  • asSequence turns a collection into a sequence.
  • A final step such as toList or first starts the work.
  • first can stop a sequence early and skip the remaining values.
  • generateSequence builds values one after another from a starting value.

In this example, the filter checks ant and then ape, and both fail the test. The filter then checks bat, which passes, so first returns bat at once. The word bee is never checked.

Try it Yourself

Edit the code, then press Run.

Running is simulated ยท Kotlin Playground

Exercise

In a sequence pipeline, when does the map step run for a value?