Various Ways to Use Kotlin Map
Map Overview
Kotlin’s Map is a collection based on Java’s Map. It maintains key-value pairs and has no concept of ordering.
Map & MutableMap
Map handles pairs of keys and values. Map is an interface that supports read-only access.
interface Map<K, out V>
MutableMap provides operations for adding and removing elements.
interface MutableMap<K, V> : Map<K, V>
Creating a Map (mapOf)
An immutable map can be created with Kotlin’s built-in mapOf function.
val map: Map<String, Int> = mapOf("AAA" to 1, "BBB" to 2, "CCC" to 3)
println(map["AAA"]) //=> 1
// Loop processing (the forEach method is also possible)
for ((k, v) in map) {
println("$k -> $v")
}
Creating a Map
An immutable map can be created with Kotlin’s built-in mapOf function.
As a simple example, let’s create a read-only Map.
fun main() {
var map = mapOf("Red" to "#f00", "Green" to "#0f0", "Blue" to "#00f")
println(map["Red"])
// Iteration processing (the forEach function is also possible)
for ((key, value) in map) {
println("$key = $value")
}
}
Output:
#f00
Red = #f00
Green = #0f0
Blue = #00f
If you want to add or remove elements after creating a Map, you must create the Map with the mutableMapOf function.
val map: MutableMap<String, Int> = mutableMapOf("A" to 1, "B" to 2, "C" to 3)
map["A"] = 5
map["D"] = 10
for ((key, value) in map) {
println("$key -> $value")
}
Output:
A -> 5
B -> 2
C -> 3
D -> 10
As you can see from this example, both changing and adding elements can be done with the expression map[key] = value (internally converted to a set(..) function call in Kotlin).
Getting Key and Value Lists from a Map (keys, values)
Map is a collection with key-value pairs, but you can get the list of keys with the keys property, or get only the list of values with the values property.
val map = mapOf("A" to 1, "B" to 2, "C" to 3)
println(map.keys)
println(map.values)
Output:
[A, B, C]
[1, 2, 3]
Changing Map Keys and Values in Bulk (mapKeys, mapValues)
Changing Map Element Keys in Bulk (mapKeys)
To change the values used as keys in an existing Map all at once, use mapKeys. When you pass a lambda expression to mapKeys, a Map.Entry object containing each element’s key and value is passed to that lambda expression sequentially. The value returned by the lambda expression in each loop is treated as the new key. In the Map.Entry object, you can refer to the element’s key with key and the element’s value with value.
Example: Converting All Map Keys to Uppercase
val map = mapOf("a" to 1, "b" to 2, "c" to 3)
val updatedMap = map.mapKeys { it.key.uppercase() }
println(updatedMap)
Output:
{A=1, B=2, C=3}
The return value of mapKeys is a Map type.
Changing Map Element Values Together (mapValues)
mapValues is similar to mapKeys, but it changes the values of Map elements in bulk. Implement the lambda expression so that the changed element value becomes the return value.
Example: Doubling All Map Values
val map = mapOf("a" to 1, "b" to 2, "c" to 3)
val updatedMap = map.mapValues { it.value * 2 }
println(updatedMap)
Output:
{a=2, b=4, c=6}
The return value of mapValues is a Map type.
Sorting and Iterating Over a Map (toSortedMap)
Sorting a Map by Key
There are cases where you need to output Map elements sorted by key. In that case, you can use toSortedMap to convert the Map to a key-sorted map (SortedMap).
val map = mapOf("C" to 3, "A" to 1, "B" to 2)
for ((k, v) in map.toSortedMap()) {
println("$k -> $v")
}
Output:
A -> 1
B -> 2
C -> 3
If you write the loop with the forEach function, readability can improve slightly because the processing flow proceeds one way from left to right.
map.toSortedMap().forEach { k, v ->
println("$k -> $v")
}
There is also a way to create a map instance as the SortedMap type from the beginning by using the factory function sortedMapOf.
val map = sortedMapOf("C" to 3, "A" to 1, "B" to 2)
map.forEach { k, v ->
println("$k -> $v")
}
Sorting a Map by Value
There may also be cases where you need to sort Map elements by value. Let’s look at a few methods.
Converting to a Key and Value List and Sorting by Value
val map = mapOf("A" to 3, "B" to 1, "C" to 2)
val pairs = map.toList().sortedBy { it.second }
pairs.forEach { (k, v) -> println("$k, $v") }
Output:
B, 1
C, 2
A, 3
This method converts the Map to List<Pair<String, Int>>, sorts it by value, and outputs it.
Passing a Custom Comparator to toSortedMap()
val map = mapOf("A" to 3, "B" to 1, "C" to 2)
val map2 = map.toSortedMap { k1, k2 ->
map[k1]!! - map[k2]!!
}
map2.forEach { (k, v) -> println("$k, $v") }
Output:
B, 1
C, 2
A, 3
This method sorts and outputs the Map by passing a Comparator to toSortedMap as a lambda expression.
Initializing a Value the First Time It Is Retrieved from a Map (Lazy Map Initialization) (getOrPut)
With the getOrPut function of MutableMap, when a value for the specified key cannot be found, you can initialize that value with a lambda expression and then return it.
val map = mutableMapOf<String, Int>()
println(map["foo"]) // null (the value does not exist)
println(map.getOrPut("foo") { 0 }) // 0 (the initial value is set at the same time as get)
println(map["foo"]) // 0 (the value is set)
Output:
null
0
0
With the getOrPut() function, you can lazily initialize map values. It can be used as a cache when you want to get fixed values in key-value form, but obtaining each value is somewhat costly (assuming the value does not change).
class UserDb {
// User age cache
private var userAge = mutableMapOf<String, Int>()
// Gets the user's age (using the cache).
fun getAge(name: String): Int = userAge.getOrPut(name) { getAgeWithoutCache(name) }
private fun getAgeWithoutCache(name: String): Int {
// Assume calculating the initial value takes time
return 10
}
}
fun main() {
val userDb = UserDb()
val age: Int = userDb.getAge("devkuma")
println(age)
}
Output:
10