Why check first
Asking a HashMap for a key it does not hold does not fail: get returns null. The trouble comes later, when that null is used. Calling a method on it, such as pets.get("Spain").length(), throws a NullPointerException. So it is often best to ask first whether the key is there.
containsKey
map.containsKey(key) returns true when the key is in the map and false when it is not. It is a boolean, so it goes in an if. Put ! in front for the opposite question:
HashMap<String, String> capitals = new HashMap<>();
capitals.put("France", "Paris");
capitals.put("Japan", "Tokyo");
if (capitals.containsKey("Japan")) {
System.out.println(capitals.get("Japan")); // Tokyo
}
if (!capitals.containsKey("Spain")) {
System.out.println("No Spain"); // No Spain
}
Inside the first if, get("Japan") is safe, because the check has just proved the key exists.
A value is not a key
containsKey looks only at keys. In the map above, capitals.containsKey("Paris") is false, because "Paris" is a value. To search the values, call containsValue("Paris"), which is true. It may have to look at every entry, so it is slower on big maps.
Keys must also match exactly, including upper and lower case: capitals.containsKey("japan") is false.
containsKey or a null check?
You could also call get and compare the result with null. The two differ when a map stores a null value: for such a key, containsKey is true but get gives null. For ordinary data without null values they agree, and containsKey says what you mean more clearly.
Adding only when absent
A common use is to add an entry only if its key is new: if (!map.containsKey(k)) { map.put(k, v); }. Without the check, put would replace the old value of an existing key. Java also has putIfAbsent(k, v) for this; when the key exists it returns the old value and changes nothing, and for a new key it adds the entry and returns null. Spelling the check out with containsKey, as this lesson does, makes the logic visible.
Speed
A HashMap finds a key without walking through the entries one by one, so containsKey stays fast even for a very large map. That is a real difference from contains on a list, which may have to compare against every item. Checking a key before using it costs almost nothing.
The exercise
The map pets goes from each pet's name to its kind: Rex is a dog, Tom a cat, Polly a parrot. Two checks:
- Is "Tom" a key? It is, so print
"Tom is a "followed bypets.get("Tom"), which iscat. - Is "cat" not a key? Use
!pets.containsKey("cat"). The word"cat"is in the map, but only as a value (Tom's kind), so it is not a key and the condition istrue.
HashMap<String, String> pets = new HashMap<>();
pets.put("Rex", "dog");
pets.put("Tom", "cat");
pets.put("Polly", "parrot");
if (pets.containsKey("Tom")) {
System.out.println("Tom is a " + pets.get("Tom"));
}
if (!pets.containsKey("cat")) {
System.out.println("cat is not a key");
}
Output:
Tom is a cat
cat is not a key
Common mistakes
- Checking a value with
containsKey. In the exercise,pets.containsKey("cat")isfalseandpets.containsValue("cat")istrue. Pick the method for the side you mean. - Wrong letter case.
pets.containsKey("tom")isfalse, because the key is"Tom". - Calling a method on the result of
getwithout checking.pets.get("Spain").length()throwsNullPointerException, becausegetgivesnullthere.
Key takeaways
containsKey(key)returnstrueorfalseand works as anifcondition.- A missing key gives
nullfromget, and using thatnullcan throwNullPointerException. - It checks keys only; use
containsValuefor values. - Keys match exactly, including letter case.
