Why check first
A Map looks values up by key. Reading a key that isn't there is an error: with capitals = Map("France" -> "Paris", "Japan" -> "Tokyo"), the call capitals("Spain") throws java.util.NoSuchElementException: key not found: Spain. So when a key might be missing, ask first.
contains(key) answers with a Boolean: true when the key is in the map, false when it isn't. That makes it a ready-made condition for an if.
val capitals = scala.collection.mutable.Map("France" -> "Paris", "Japan" -> "Tokyo")
if (capitals.contains("Japan")) {
println(capitals("Japan")) // Tokyo
}
if (!capitals.contains("Spain")) {
println("No Spain") // No Spain
}
Inside the first if, capitals("Japan") can't throw, because the check just proved the key is there. Put ! in front of contains for the opposite question: "is this key missing?"
A value is not a key
contains looks only at the keys. Here "Paris" is a value, so capitals.contains("Paris") is false. A key also has to match exactly, capital letters included: capitals.contains("japan") is false.
Another way to ask
get(key) returns an Option: capitals.get("Japan") is Some(Tokyo) and capitals.get("Spain") is None. It is the right tool when you want the value if it exists. When you only want a yes or no, contains says it directly.
Check, then read
The pattern in the first example is worth remembering: contains in the condition, map(key) in the body. The check protects the read. Code that reads without checking works for every key you tried and fails on the first one you didn't, which is how an exception shows up late. Another way is getOrElse, when a default value does the job.
The same contains works on a Map that you can't change, such as one made with plain Map(...). The lesson uses a mutable one, so you can also add and delete entries later.
The task
In pets, each key is a pet's name and its value is the pet's kind. The task has two checks.
- If
"Tom"is a key, printTom is afollowed by Tom's kind. Tom is a key, and his kind is"cat". - If
"cat"is not a key, printcat is not a key. The word"cat"is in the map, but only as Tom's value, socontains("cat")isfalseand the!makes the condition true.
val pets = scala.collection.mutable.Map("Rex" -> "dog", "Tom" -> "cat", "Polly" -> "parrot")
if (pets.contains("Tom")) {
println("Tom is a " + pets("Tom"))
}
if (!pets.contains("cat")) {
println("cat is not a key")
}
It prints:
Tom is a cat
cat is not a key
Two keys that look alike
Keys are compared as whole values. With the pets map, "Tom" is a key but "Tom " with a trailing space is not, and "Tomas" is not either. If a lookup fails when the name looks right, print the key and check for stray spaces or different capitals.
Common mistakes
- Reading before checking. Writing
pets("Sam")for a name that may be missing throws an exception and stops the program. Check withcontainsor use another lookup that handles a missing key. - Checking a value.
pets.contains("cat")is a question about keys. It does not find the value"cat". - Case and spelling.
"tom"and"Tom"are different keys.
Key takeaways
map.contains(key)istruewhen the key is in the map andfalseotherwise.- Reading a missing key with
map(key)throws, so check first when it might be absent. - Only keys count: a value is not found by
contains. !map.contains(key)asks whether the key is missing.
