by Adam Arold
Exploring Kotlin: useful standard library functions
As others have written before, Kotlin comes with a lot of handy functions like
also. Less is written about what comes with the
ranges, and other packages of the standard library. I think that a lot more can be done using just the Kotlin standard library, so let's explore it in depth!
Kotlin comes with
Triple which are basic generic tuples:
Java does not have them, so you might ask why are these useful?
Ever wanted to return two values from a function? With
Pairs, this is rather easy to accomplish: you just have to use it as a return type. What’s more useful is that we can use destructuring to split a
Pair into two values:
We can put tuples to good use when we work with
Maps as well.
Pair comes with an useful
to , which lets us create a
Pair like this:
Then we can use this syntax to create
Maps in a much more readable way:
If you come from Java, these might be a bit odd at first. But once you get used to it, using
infix functions and tuples will become second nature!
If you have worked with the Kotlin stdlib for a while, you probably bumped into a bunch of library functions which are either improvements over the Java versions or new additions by Kotlin. You have
reduce , for example, which are defined on
Iterable objects. And there are a bunch of others which are defined on immutable
Note that from now on we’ll talk about operations which are defined for immutable collections.
Creating collections has never been easier. We have
mapOf and even
arrayOf to create the corresponding collection.
What’s interesting is that most of these operations are also defined for
Note that a
Map in Kotlin does not implement the
Collection interface, but defines some operations for
Maps which are counterparts for the ones in
Maps as collections of
Pairs, and we can create
Maps from any collection which holds them:
In addition to
filter , and
reduce we also have
mapKeys will create new
Maps when we call them. They are useful when we have a
Map and we only want to transform either the keys or the values. The
filter variants follow the same logic, but with filtering. We can also combine them:
If we want to perform operations other than these, we can turn our
Map into a collection by calling
These operations might be a bit odd if you come from Java, but after a while it makes sense if you start to think about
Maps as a sequence of key-value
In addition to
toMap , and other conversion functions, there are also some specialized ones which are defined on only some selected types like
Char. For example, we can turn a
Bytes to a
ByteArray like this:
There is a
to*Array defined for each primitive type. They all return a corresponding
*Array type which is optimized.
Note that these collections in Kotlin are not immutable, in fact it is only the interface which does not allow mutation.
There are some pitfalls to this. Take a look at my other article about the topic
where I explain this problem.
Immutable collections are perfect for functional programming, since every operation defined on them returns a new version of the old collection without changing it. This also means that they are safe from a concurrency perspective, since we don’t need locks to work with them. A problem, though, is that we lose operations like
Luckily, most operations which work with mutable collections have an immutable counterpart.
minus work like
remove and we also have
union , and
intersect. They work like
addAll , and
Drop and take
We can also work with collections in the same way offset and limit work in RDBMSes.
drop will return with a
List without the first
dropLast works in the same way, but drops elements from the end:
There is also
dropLastWhile which drops elements until a certain condition is met:
For all of the above functions, there is a
take variant which works like
drop but it takes elements:
If you come from LISP, these functions might be familiar to you: they are like
rest in Clojure, for example.
In Kotlin you can define
Calculating distinct values
These are useful, but sometimes we only want to pick distinct values. We can do so by calling
distinctBy , we can write our own selector function:
We’ve already seen ways we can turn
Lists, but can we do it the other way around? The answer is yes. Kotlin comes with
associate , and
associateBy which lets us split our
Lists in different ways.
groupBy separates our
List into multiple
Lists grouped by keys, so the result is a multimap. We only need to provide a key selector function to do so. In this example, we group a
Ints into even and odd groups:
associate is different in the following ways: it transforms each element to a key-value pair, and if multiple values map to the same key only the last one is returned. In our previous list of
Ints which are sorted, this will effectively give us the greatest odd and even numbers:
A variant to
associateBy , which does not transform the original values but takes a key selector function. If multiple elements would have the same key, only the last one is added to the resulting
Map. This is an example which does the same as the previous one but with
partition is a special transformation function which groups to only a
Lists based on the result of a predicate:
We’ve seen how we can split things, but let’s see what we have for joining them!
zip will work exactly like the zipper of your trousers: it zips two
Lists into a
We can be a bit more sophisticated if we provide a transform function to
zipWithNext will pair each element with the next:
and it can also take a transform function:
Sometimes we want to transform our collections to a
String representation. This is useful if we want to log the contents of them, for example. For this purpose, we have
joinTo , which takes an
Appendable and some extra arguments (like a separator) and returns the
Appendable with the contents of the collection appended:
Since joining to a
String is so common, we also have
As you have seen from the previous examples, Kotlin collections work a bit differently from those in Java. When we use Java, we have the Stream API which lets us perform most of these operations — but they come at a price: we need to convert between streams and collections, and they also come with more boilerplate.
Kotlin does not differentiate between streams and collections. All of the above funtions are defined for
Lists. This lets us write programs more fluently, and at the end of the day we’ll end up with more readable code by doing less work.
Immutable collections are an added bonus: they help us write code which is less error-prone, without the need to write more. And since we can’t mutate them, we’ll have less concurrency issues, like race conditions or deadlocks.
The examples above are far from exhaustive, but there are some interesting functions which are really useful sometimes. For example, we have the most commonly used
String transformations as extension functions:
There are also
Ranges which are very useful for iteration. We can create them directly from numbers with useful
We’ve only scratched the surface with the examples above, but I hope that you now have an idea about what the Kotlin stdlib has to offer. I strongly encourage you to open your IDE and take a look at these functions from the source. They are documented very well, so you can get started in no time.
Thanks for reading! You can read more of my articles on my blog .