Sequences — Terminal Operations
Kotlin sequences are evaluated lazily — they are not processed until a terminal operation is called. Terminal operations such as toList(), first(), and take() convert a sequence into a concrete value or collection.
Syntax
val seq = generateSequence(1) { it + 1 } // infinite sequence
// toList — converts to a list (always limit with take first)
val list = seq.take(5).toList() // [1, 2, 3, 4, 5]
// first / last — first/last element
val first = seq.take(10).first() // 1
val firstEven = seq.first { it % 2 == 0 } // 2
// takeWhile / dropWhile — control with a condition
val lessThan5 = seq.takeWhile { it < 5 }.toList() // [1, 2, 3, 4]
Method List
| Terminal Operation | Description |
|---|---|
| sequence.toList() | Converts the entire sequence to a list. For infinite sequences, call take() first. |
| sequence.toSet() | Converts the sequence to a set. |
| sequence.take(n) | Returns a sequence of the first n elements. Also acts as an intermediate operation. |
| sequence.takeWhile { condition } | Returns elements while the condition is true. Stops as soon as it becomes false. |
| sequence.drop(n) | Skips the first n elements. |
| sequence.dropWhile { condition } | Skips elements while the condition is true, then returns the rest. |
| sequence.first() | Returns the first element. |
| sequence.first { condition } | Returns the first element matching the condition. |
| sequence.firstOrNull { condition } | Returns the first element matching the condition, or null if none is found. |
| sequence.count() | Returns the number of elements. Only use with finite sequences. |
| sequence.sum() / average() | Calculates the sum or average. Only use with finite sequences. |
| sequence.forEach { } | Executes an action for each element. |
Sample Code
fun main() {
// infinite sequence of natural numbers
val naturals = generateSequence(1) { it + 1 }
// take — first N elements
println("First 10: ${naturals.take(10).toList()}")
// takeWhile — while the condition is true
println("Less than 10: ${naturals.takeWhile { it < 10 }.toList()}")
// combining drop and take (pagination)
val page2 = naturals.drop(10).take(5).toList() // 11–15
println("Page 2: $page2")
println()
// first — first element satisfying a condition
val firstSquare = naturals.first { it * it > 50 }
println("First square greater than 50: $firstSquare × $firstSquare = ${firstSquare * firstSquare}")
// firstOrNull — returns null if not found
val seq = sequenceOf(1, 3, 5, 7, 9)
println("Even number: ${seq.firstOrNull { it % 2 == 0 }}") // null
println()
// prime number sequence (simplified Sieve of Eratosthenes)
fun primes(): Sequence<Int> = sequence {
val sieve = mutableSetOf<Int>()
var n = 2
while (true) {
if (n !in sieve) {
yield(n)
sieve.addAll(generateSequence(n * n) { it + n }.takeWhile { it < 1000 })
}
n++
}
}
println("First 10 primes: ${primes().take(10).toList()}")
println("Primes up to 50: ${primes().takeWhile { it <= 50 }.toList()}")
println()
// use asSequence to process a list efficiently
val data = (1..10000).toList()
val result = data.asSequence()
.filter { it % 3 == 0 } // multiples of 3
.map { it * it } // square
.take(5) // first 5 results
.toList()
println("Squares of multiples of 3 (5 results): $result") // [9, 36, 81, 144, 225]
}
Notes
takeWhile() and dropWhile() are essential for controlling sequence boundaries by condition. Because they stop (takeWhile) or start (dropWhile) the moment the condition becomes false for the first time, they are well suited for extracting ranges from sorted data or controlling infinite sequences.
Intermediate sequence operations (such as filter, map, and take) return a new sequence object, while terminal operations (such as toList, first, and count) return a concrete value. No processing occurs until a terminal operation is called.
For creating sequences, see sequenceOf() / generateSequence(). For asynchronous streams using coroutines, see Flow — Basics.
If you find any errors or copyright issues, please contact us.