Kotlin's official "collections" page contains the following remark:
Map creation in NOT performance-critical code can be accomplished with simple idiom: mapOf(a to b, c to d).
Questions:
a/ What is the reason behind this remark (the best explanation I could come up with is that the "a to b" expressions create an extra, transient, Pair object, but I am not sure).
b/ What is the suggested best practice for initializing a map in a way that is suitable for performance-critical code?
There are two things that happen under the hood that might affect performance:
The mapOf(...)
function expects a vararg
of pairs, and, during a call, an array is created for the arguments and then passed to the function. This operation involves allocating an array and then filling it with the items.
As you correctly noted, the a to b
infix function creates a pair (equivalent to Pair(a, b)
), which is another object allocation.
Allocating many objects affects performance when done many times in tight loops (including additional load on garbage collector, when dealing with short-living objects).
Additionally, using an array for the vararg
may affect locality of reference (instead of passing the arguments through the stack, they are placed into a separate memory region located somewhere else in the heap).
While JVMs are usually good at local optimizations and can sometimes even eliminate allocations, these optimizations are not guaranteed to happen.
A more performant way to create a map and fill it with items is:
val map: Map<Foo, Bar> = HashMap().apply {
put(a, b)
put(c, d)
}
Using apply { ... }
introduces no overhead since it is an inline function.An explicit type annotation Map<Foo, Bar>
shows the intention not to mutate the map after it is created.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With