Start learning free

LESSON 130 OF 677 · DICTIONARIES

Reading a Missing Key with getOrDefault in Kotlin

Lesson Read the full explanation

TL ; DR:

map.getOrDefault(key, 0) gives the key's value, or 0 when the key is missing.


val points = mutableMapOf("Ana" to 7)

println(points.getOrDefault("Ana", 0))   // 7
println(points.getOrDefault("Ben", 0))   // 0


Full lesson:

A missing key often means "none yet". A player with no points yet has 0 points.

points["Ben"] gives null here. Its type is Int?, a number or null. And !! on null crashes the program.

getOrDefault takes a key and a default. It gives the key's value when the key is there. It gives the default when it isn't. Either way, you get a plain Int.

Reading this way doesn't add the key. points still has one key afterwards.

The Elvis operator from Null Safety works too, as in points["Ben"] ?: 0.


Your task

stock counts the fruit in a shop. The code reads and prints the pears.

There are no plums yet. Read "plums" with getOrDefault and 0 as the default. Store it in plums.

Then print the plums line, the same way as the pears line.

Expected output:

We have 2 pears
We have 0 plums
STEP 1 OF 2
    Ctrl + Enter to check

    KOTLIN · DICTIONARIES

    Dictionary Default Value in Kotlin, explained

    The problem with a missing key

    A missing key often just means "none yet": a shop with no plums has 0 plums. But on a Kotlin map, reading a missing key gives null:

    val stock = mutableMapOf("apples" to 5)
    val t: Int? = stock["plums"]
    println(t)
    
    null
    

    The type is Int?, a number or null, so you can't use it in arithmetic until you deal with the null. Forcing it with !! crashes the program when the key is missing.

    getOrDefault

    getOrDefault(key, default) gives the key's value when the key is there and the default when it isn't. Either way the answer is a plain Int, never null.

    println(stock.getOrDefault("apples", 0))
    println(stock.getOrDefault("plums", 0))
    println(stock.size)
    
    5
    0
    1
    

    Reading this way doesn't add the key: stock still has one entry afterwards. The default is only what you get back, not something the map stores.

    Other ways to say the same thing

    • The Elvis operator. stock["plums"] ?: 0 uses the right side when the left is null. It gives 0 here too.
    • getOrElse. stock.getOrElse("plums") { -1 } takes a small block instead of a value, which is handy when computing the default takes some work. It gives -1 here.
    • getOrPut. stock.getOrPut("dan") { 0 } on a mutable map also gives 0 for a missing key, but it stores that default as well, so the map grows. Use it only when you do want the key added.

    Pick getOrDefault when you just want a number to calculate with and the map should stay as it is.

    Traps

    • The default has to fit the value type. For a map of Int counts, 0 is a natural default; for text, an empty string "" may be.
    • A key that is present keeps its own value, even if that value is 0. The default only applies to missing keys.
    • Keys match exactly, so "Plums" is a different key from "plums".

    A common use: counting

    A default of 0 is what makes counting short. Each time a letter appears, read its count so far (0 if it is new), add 1 and store the result:

    val letters = mutableMapOf<Char, Int>()
    for (c in "banana") letters[c] = letters.getOrDefault(c, 0) + 1
    println(letters)
    
    {b=1, a=3, n=2}
    

    Without getOrDefault you would need an if to handle the first time a letter shows up. Notice that the read and the write are separate steps: getOrDefault only reads, and the assignment is what adds the key.

    For a map of text values the default is text too: words.getOrDefault("b", "none") gives none when "b" is missing, and words.getOrDefault("b", "") gives an empty string.

    The exercise

    stock holds apples and pears. The starter code reads the pears with stock["pears"]!!, which is safe because the key is there, and prints the pears line. For the plums, the key is missing, so step 1 uses getOrDefault with 0, and step 2 prints the line the same way as the pears line.

    val stock = mutableMapOf("apples" to 5, "pears" to 2)
    val pears = stock["pears"]!!
    println("We have " + pears + " pears")
    val plums = stock.getOrDefault("plums", 0)
    println("We have " + plums + " plums")
    
    We have 2 pears
    We have 0 plums
    

    Key takeaways

    • A missing key reads as null, with the type Int?.
    • getOrDefault(key, default) gives a plain value and leaves the map unchanged.
    • ?: default and getOrElse are alternatives; getOrPut also stores the default.
    Keep going with Kotlin500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free