Kotlin has brought us new ways to write iterators by implementing directly functional methods like map, fold, filter, and plenty of others on the Iterable interface. Since Kotlin developers can use the Kotlin standard library and the Java standard library when working on the JVM, it can be confusing to decide which one to use, as Java has many useful features, such as Java Streams.
Let’s look at a few of the popular Iterators options available to solve a simple problem: count integers occurrences in a list, and we will generalize from there to decide which option you should use and when.
For example, given the list (1,2,2,3), the method should return {1: 1, 2: 2, 3: 1}.
As a developer, you want to write code that is easy to understand, easy to maintain, and performant, these will be the criteria of our evaluation.
Algorithmically speaking, it appears that a good solution is to go through the elements of the list, keeping a map of the items seen so far and increment it for each item since this algorithm has a time complexity in O(n).
Let’s implement this solution in Java:
The method’s code is quite long meaning it could be difficult to maintain and difficult to understand, still, the code only uses one basic API (HashMap) and does not require any specialized knowledge to be understood.
Let’s implement the same method in Kotlin:
In Kotlin, the code still only uses one API (HashMap) and is very similar to its Java counterpart.
Streams brought to Java the ability to work on Iterables with functional programming style methods. They work great because they are asynchronous by default and allow the collector to compute only what it needs.
There isn’t too much code produced but reading it requires knowledge of Streams and Collectors that might be a bit confusing for developers that are new to Java. The code is easy to maintain.
A great feature with Java stream is parallelStream that allows developers to run the execution on all available cores when it is possible without any specific implementation.
Kotlin functional methods on the Iterable interface is the simplest way to iterate in Kotlin. It is very intuitive as it simplifies the functional style of Java Streams. It does not include the complexity of Streams and Collectors as the functional methods are implemented directly on the Iterable class.
This option produces the best code. It is concise, very expressive, and maintainable. Performance-Wise, it is run synchronously, and the performance can be improved a bit if run asynchronously thanks to the asSequence method.
To determine which option is the fastest in terms of performances I did the test on my machine: Macbook Pro 2018 with 2.6 GHz Intel Core i7 and 16 GB 2400 MHz DDR4 under Mac OS Mojave 10.14.5
I measured the time of execution of each method with various list sizes and averaged it by doing it ten times to limit the variance. You can replicate the test using the code on the github.
After experimenting with the different Iterable methods, we get the following results (the lower the time, the better the performance).
We find the following results with small list sizes:
When having to work with Iterators on Kotlin, you should go with Kotlin Iterables as it is the option that produces the cleanest code, since it is easy to understand, to maintain, and to write.
Sometimes developers deal with a critical and extensive computation and face performance issues, in this case, they should optimize their solutions in terms of performances, Java Streams offer a great way to parallelize at the price of a code that is less readable for Kotlin developers. Java Parallel Streams limit being that it can only parallelize on one machine.
The Java Parallel Stream should boost your application, but if that is still not enough, you should consider clustering up your computation with a solution such as Apache Spark.