A missing key often means "none yet"
In a shop's stock count, a fruit that is not in the map simply means the shop has none. The natural answer for "how many plums?" is 0, not an error. But stock.get("plums") returns null for a missing key, and a null cannot be stored in an int. Java tries to unbox it and throws a NullPointerException:
HashMap<String, Integer> stock = new HashMap<>();
stock.put("apples", 5);
stock.put("pears", 2);
System.out.println(stock.get("plums")); // null
int plums = stock.get("plums"); // NullPointerException
getOrDefault
map.getOrDefault(key, fallback) takes a key and a default. It returns the key's value when the key is in the map, and the default when it is not.
System.out.println(stock.getOrDefault("apples", 0)); // 5
System.out.println(stock.getOrDefault("plums", 0)); // 0
Reading this way does not add the key. After the calls above, stock.size() is still 2.
The same thing with containsKey
Before getOrDefault, the usual way was an if with containsKey:
int plums = 0;
if (stock.containsKey("plums")) {
plums = stock.get("plums");
}
getOrDefault is that whole block in one line: int plums = stock.getOrDefault("plums", 0);.
The null check, spelled out
A third way is to keep the result in an Integer, which can hold null, and fix it by hand: Integer n = stock.get("plums"); if (n == null) { n = 0; }. That leaves n as 0 here. It works, but it is more code than getOrDefault for the same idea, and easy to get wrong if you declare n as an int by mistake, since an int can never be compared with null.
Choosing the default
The default can be any value of the map's value type. For counts it is 0. For a HashMap<String, String> it could be a text such as "unknown". Pick the value that means "nothing yet" for your data.
Details
The default is returned only when the key is not in the map. If the key is present with a null value, getOrDefault returns that null, not the fallback. Maps of counts normally never hold null, so this rarely matters. On a map with string values, fl.getOrDefault("x", "unknown") on an empty fl returns unknown.
The exercise
The map stock holds 5 apples and 2 pears. The pears are read and printed with get, which is safe because the key exists. There are no plums, so:
- Read the plums with
stock.getOrDefault("plums", 0)and store the result in anintnamedplums. It is 0. - Print the line the same way as the pears line, with
plumsin place ofpears.
HashMap<String, Integer> stock = new HashMap<>();
stock.put("apples", 5);
stock.put("pears", 2);
int pears = stock.get("pears");
System.out.println("We have " + pears + " pears");
int plums = stock.getOrDefault("plums", 0);
System.out.println("We have " + plums + " plums");
Output:
We have 2 pears
We have 0 plums
Common mistakes
- Using
getfor a key that may be missing. Printingstock.get("plums")showsnull, and putting it in anintcrashes. - Expecting the key to be created. After
getOrDefault("plums", 0),containsKey("plums")is stillfalseand the size has not changed. To store the key, callput. A counter that reads with a default and writes withputis a very common pattern:stock.put("apples", stock.getOrDefault("apples", 0) + 1)changesapplesfrom 5 to 6. - A default of the wrong type. The fallback must fit the map's value type: an
intforIntegervalues, a string for string values.
Key takeaways
getreturnsnullfor a missing key, and unboxing thatnullinto anintthrowsNullPointerException.getOrDefault(key, fallback)returns the value, or the fallback when the key is missing.- It does not add the key to the map.
- It replaces the
containsKeycheck plusgetwith one call.
