Start learning free

LESSON 129 OF 677 · DICTIONARIES

Checking for a Key in a Kotlin Map

Lesson Read the full explanation

TL ; DR:

Write key in map to ask whether a map has a key. The answer is true or false, so it goes in an if.


val capitals = mutableMapOf("France" to "Paris", "Japan" to "Tokyo")

if ("Japan" in capitals) {
    println(capitals["Japan"])   // Tokyo
}

if ("Spain" !in capitals) {
    println("No Spain")          // No Spain
}


Full lesson:

A missing key gives null. capitals["Spain"] is null, and !! on it crashes the program.

So check first. On a map, in is short for containsKey. The check "Spain" in capitals is true when "Spain" is a key.

!in asks the opposite. "Spain" !in capitals is true here.

A value is not a key

in looks only at the keys. "Paris" in capitals is false, because "Paris" is a value.

To look in the values, write "Paris" in capitals.values.

A key must match exactly, upper and lower case too. "japan" in capitals is false.


Your task

In pets, each key is a pet's name, and its value is the pet's kind.

First, check whether "Tom" is a key of pets. If it is, print Tom is a followed by Tom's kind.

Then check that "cat" is not a key, with !in. If so, print cat is not a key.

"cat" is in pets, but only as a value. It is Tom's kind.

Expected output:

Tom is a cat
cat is not a key
STEP 1 OF 2
    Ctrl + Enter to check

    KOTLIN · DICTIONARIES

    Dictionary Key Check in Kotlin, explained

    Why check first

    Reading a key that isn't in a map doesn't throw. It gives null, and the type of map[key] is the nullable String? or Int?. That is fine until you add !! to force a plain value: on null it crashes the program with a NullPointerException. So when a key might be missing, ask the map first.

    in and !in

    On a map, key in map is short for map.containsKey(key). It gives true when the key is there and false when it isn't, so it goes right into an if. !in asks the opposite.

    val capitals = mutableMapOf("France" to "Paris", "Japan" to "Tokyo")
    println(capitals)
    if ("Japan" in capitals) {
        println(capitals["Japan"])
    }
    if ("Spain" !in capitals) {
        println("No Spain")
    }
    
    {France=Paris, Japan=Tokyo}
    Tokyo
    No Spain
    

    Printing a map shows each entry as key=value, so capitals["Spain"] would print null, while capitals["Spain"]!! would stop the program.

    A value is not a key

    The in operator looks only at the keys. "Paris" in capitals is false, because "Paris" is a value. To look through the values, ask for them: "Paris" in capitals.values is true, and capitals.containsValue("Paris") says the same.

    A key also has to match exactly, upper and lower case too. "japan" in capitals is false when the key is "Japan".

    in or a null test?

    capitals["Japan"] != null gives the same answer here, but in says what you mean and it keeps working for a map whose values are allowed to be null. In such a map a key can be present with a null value, and the read can't tell that apart from a missing key. Only in (or containsKey) can.

    For a map like capitals whose values are never null, either test is fine, so choose the one that reads best.

    Quick reference

    For capitals above, the questions and their answers are:

    ExpressionResult
    "Japan" in capitalstrue
    "Spain" in capitalsfalse
    "Spain" !in capitalstrue
    "Paris" in capitalsfalse
    "Paris" in capitals.valuestrue
    "japan" in capitalsfalse

    A first use: update or create

    The check lets you treat known and unknown keys differently. Counting words is the classic case: if the word is already a key, add 1 to its count; otherwise start it at 1.

    val counts = mutableMapOf<String, Int>()
    for (w in listOf("a", "b", "a")) {
        if (w in counts) counts[w] = counts[w]!! + 1 else counts[w] = 1
    }
    println(counts)
    
    {a=2, b=1}
    

    Here !! is safe, because the if has just proved the key is there. That is the pattern to remember: check with in, then read.

    The exercise

    pets maps each pet's name to its kind: Rex is a dog, Tom is a cat and Polly is a parrot. Step 1 checks "Tom" in pets and prints "Tom is a " plus pets["Tom"]. Step 2 checks "cat" !in pets. The word "cat" only appears as Tom's kind, which is a value, so it is not a key and the check is true.

    val pets = mutableMapOf("Rex" to "dog", "Tom" to "cat", "Polly" to "parrot")
    if ("Tom" in pets) {
        println("Tom is a " + pets["Tom"])
    }
    if ("cat" !in pets) {
        println("cat is not a key")
    }
    
    Tom is a cat
    cat is not a key
    

    The string join works without !! because + turns a String? into text. If the key were missing, it would print the word null, which is another reason to check first.

    Notice that the two checks are independent. The first if reads a value, so it needs the key to exist; the second only asks a question and never reads anything. Swapping the second check to "cat" in pets would be false for the same reason: the map has no key called "cat", however many animals are cats.

    Key takeaways

    • A missing key reads as null, and !! on it crashes.
    • key in map is containsKey; key !in map is the opposite.
    • in looks at keys only; use map.values to search values.
    • Keys must match exactly, including case.
    Keep going with Kotlin500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free