Which iterators should you pick as a Kotlin developer?

4
minutes
Mis à jour le
15/9/2019

Share this post

You will learn which option to choose when working with Iterators in Kotlin: Custom Algorithm, Kotlin Iterables, Java Streams, etc.

#
Algorithms
#
Kotlin
#
Performance

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.

Implementing your algorithm

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.

Java 8 Streams

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 Iterables

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.

The speed test

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).

Kotlin Iterators

We find the following results with small list sizes:

Kotlin Iterators (1)

What we learned:

  • The parallel solution aside, optimization for this problem is not obvious. For example, we could expect Kotlin Hashmap and Java Hashmap to produce the same bytecode and hence to have the same execution time while for an extensive list, it appeared that the Java Hashmap was the best single-core performing solution while Kotlin Hashmap was the worst.
  • Java 8 Stream is excellent for parallelization. Once there is a Java 8 Stream implementation, the parallelization on a machine is simple, the developer needs to replace stream into parallelStream to make huge performance gain while it would be possible for custom implementation, the cost of development would be much higher.
  • The simplest solution is Kotlin Iterables, its syntax being the shortest, the most understandable while remaining maintainable. Performance-wise, it is in the average of other single-core implementations. The only problem with this solution is the impossibility to run the method on more than one CPU.

Conclusion

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.