Kotlin, collections and sequences

When streams were added to Java 8, I wanted to jump on the bandwagon. I read blog posts, watched videos, attended workshops to understand what this was all about. After a while, I became comfortable enough…​ but I was unhappy with the implementation: while streams offer methods with a functional approach, legacy collections do not. When one wants to use Java capabilities on an existing collection, the later needs to be transformed to a stream, and then back again.

When streams were added to Java 8, I wanted to jump on the bandwagon. I read blog posts, watched videos, attended workshops to understand what this was all about. After a while, I became comfortable enough…​ but I was unhappy with the implementation: while streams offer methods with a functional approach, legacy collections do not.

When one wants to use Java capabilities on an existing collection, the later needs to be transformed to a stream, and then back again. For example, the following code filters out some elements from a collection, through a streaming step:

List<String> names = Arrays.asList("Joe", "Jack", "William", "Averell");
List<String> jNames = names.stream()
                           .filter(name -> name.startsWith("J"))
                           .collect(Collectors.toList());

On the other hand, Kotlin collections natively offer functional capabilities:

val names = listOf("Joe", "Jack", "William", "Averell")
val jNames = names.filter { it.startsWith("J") }

That’s the reason I thought it was a better approach (notice the past tense here). There’s a big catch, though: Kotlin functional functions are not lazily-evaluated, while Java stream’s functions are! This is perfectly fine if the collection contains only a few elements, but it can be a huge issue if there are many elements - or many steps in the processing pipeline.

Yet, Kotlin also provide a lazily-evaluated object: the Stream counterpart is Sequence:

class diagram
Common functions between collections and sequences

Though they share common functions, there’s no shared interface between Kotlin collections and Sequence. Their functions are generated through a macro-like script (see GenerateStandardLib for more details). For example, Sequence functions are defined as extensions in the generated kotlin.sequences._Sequences.kt file.

Conclusion

If a collection contains more than a few elements, or is bound to, then sequences should be preferred over collections. Otherwise, it’s perfectly fine to use standard collections, as they offer the same capabilities.