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?