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
:
Common functions between collections and sequences
Though they share common functions, there’s no shared interface between Kotlin collections and |
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.