Kotlin Iterable vs Sequence

Kotlin, known for its expressive syntax and pragmatic approach, has an intriguing detail in its collections framework—both Iterable and Sequence exist as interfaces for iterating over elements. The presence of these two often raises questions, as Iterable is widely used, while Sequence tends to remain in its shadow, rarely mentioned.

Iterable vs Sequence

Let’s explore the difference between Iterable and Sequence with a quick guessing game. Consider the following two functions. They look almost identical, but their behavior is fundamentally different:

fun iterable(): Iterable<Int> {
    val list = listOf(1, 2, 3, 4, 5)
    return list
        .map { println("Mapping $it"); it * 2 }
        .filter { println("Filtering $it"); it > 5 }
}

fun sequence(): Sequence<Int> {
    val sequence = sequenceOf(1, 2, 3, 4, 5)
    return sequence
        .map { println("Mapping $it"); it * 2 }
        .filter { println("Filtering $it"); it > 5 }
}

Both functions do the same thing, right? They take a list of numbers, double each one, and then filter out those that are not greater than 5. But wait, here’s the twist: when you run these functions, they behave very differently.

A Surprising Observation

fun main() {
    println("Using Iterable:")
    iterable()
    println("Using Sequence:")
    sequence()
}

When running this code, the output might surprise anyone who assumes Iterable and Sequence behave the same way:

Using Iterable:
Mapping 1
Mapping 2
Mapping 3
Mapping 4
Mapping 5
Filtering 2
Filtering 4
Filtering 6
Filtering 8
Filtering 10

Using Sequence:

Wait, what just happened? The iterable() function produces a series of outputs as it maps and filters the elements. But sequence()? Not a single line is printed!

Why Does This Happen?

Adding toList() to the Sequence

fun main() {
    println("Using Iterable:")
    iterable()

    println("\nUsing Sequence:")
    sequence().toList()
}

The New Output

Using Iterable:
Mapping 1
Mapping 2
Mapping 3
Mapping 4
Mapping 5
Filtering 2
Filtering 4
Filtering 6
Filtering 8
Filtering 10

Using Sequence:
Mapping 1
Filtering 2
Mapping 2
Filtering 4
Mapping 3
Filtering 6
Mapping 4
Filtering 8
Mapping 5
Filtering 10

With the addition of toList(), the Sequence is now evaluated, and its map and filter operations are triggered. However, the way the output appears reveals a key difference between Iterable and Sequence:

Playground

To see this difference in action, these examples can be tested directly using this link to the Kotlin Playground.