getOrDefault()
Read a value with a fallback for missing keys, and avoid the three traps: null values, eager defaults and defaults that are never stored.
default V getOrDefault(Object key, V defaultValue)- Added in Java Java 8
Parameters
| Parameter | Type | Description |
|---|---|---|
$key | Object | The key to look up. |
$defaultValue | V | The value to return when the key has no mapping. Evaluated before the call, even when the key is present. |
Return value
VThe value mapped to the key, or defaultValue if the map contains no mapping for the key.
Errors
ClassCastException if the key's type is not compatible with the map (optional); NullPointerException if the key is null and the map does not permit null keys.
getOrDefault(key, fallback) returns the stored value, or the fallback when the key is missing. It replaces the get, null check, assign pattern and removes the unboxing NullPointerException you get from int n = map.get(key) on a miss. It was added to Map as a default method in Java 8.
Three traps
A null value is not a missing key. The fallback is used only when there is no mapping. If the key is present with a null value, getOrDefault returns null. This bites code that reads JSON into a Map<String, Object>, where "nickname": null is a real entry.
The fallback is always evaluated. It is an ordinary argument, so Java computes it before the method runs, whether the key is present or not. cache.getOrDefault(id, loadFromDatabase(id)) queries the database on every call, which defeats the cache. Use computeIfAbsent to load and store on a miss, or Optional.ofNullable(map.get(id)).orElseGet(() -> load(id)) to load without storing.
The fallback is not stored. getOrDefault never changes the map. This line looks right but adds to a throwaway list:
tagsByPost.getOrDefault(postId, new ArrayList<>()).add(tag); // map unchanged
tagsByPost.computeIfAbsent(postId, k -> new ArrayList<>()).add(tag); // stored
Counting
counts.put(k, counts.getOrDefault(k, 0) + 1) works on a single-threaded map, but it is two operations. On a ConcurrentHashMap another thread can update the key in between and one increment is lost. counts.merge(k, 1, Integer::sum) is one call and is atomic on ConcurrentHashMap.
On the back end
Good uses are read-only lookups with a real default: a rate limit per plan with a fallback for unknown plans, a feature flag that defaults to off, a content type that defaults to application/octet-stream. If the default hides a configuration mistake you would rather catch, a missing key should fail loudly instead.
Example
When to use it
- Reading a per-plan rate limit with a safe fallback for plans added after the table was written.
- Returning a default content type for file extensions that a static file handler does not know.
- Reading an optional query parameter from a parsed parameter map with a default page size.
Frequently asked questions
Does getOrDefault add the default value to the map?
No. It only returns the default; the map is unchanged. Use computeIfAbsent or putIfAbsent if you want the value stored.
Why does getOrDefault return null?
Because the key is present and mapped to null. The default is only used when there is no mapping at all.
What is the difference between getOrDefault and computeIfAbsent?
getOrDefault evaluates the default eagerly and never stores it. computeIfAbsent runs its function only when the key is missing, stores the result and returns the value now in the map.
More examples
A null value is returned, not the default
The nickname key exists, so its null value comes back unchanged. When both missing keys and null values should fall back, test the result of get for null instead.
void main() {
Map<String, String> profile = new HashMap<>();
profile.put("nickname", null);
IO.println(profile.getOrDefault("nickname", "(none)"));
IO.println(profile.getOrDefault("avatar", "(none)"));
String nick = profile.get("nickname");
IO.println(nick != null ? nick : "(none)");
}
// Output:
// null
// (none)
// (none)The default is never stored
The first add goes into a list the map never sees. computeIfAbsent stores the new list and returns it, so the add lands in the map.
void main() {
Map<String, List<String>> tagsByPost = new HashMap<>();
tagsByPost.getOrDefault("post-1", new ArrayList<>()).add("java");
IO.println(tagsByPost);
tagsByPost.computeIfAbsent("post-1", k -> new ArrayList<>()).add("java");
IO.println(tagsByPost);
}
// Output:
// {}
// {post-1=[java]}The default argument always runs
The key was cached, yet getOrDefault still ran the load once. orElseGet takes a supplier, which only runs on a miss, so the count stays at 1.
int lookups = 0;
String loadFromDatabase(String key) {
lookups++;
return "db:" + key;
}
void main() {
Map<String, String> cache = new HashMap<>();
cache.put("user:1", "cached:ada");
String v = cache.getOrDefault("user:1", loadFromDatabase("user:1"));
IO.println(v + " (lookups=" + lookups + ")");
String w = Optional.ofNullable(cache.get("user:1"))
.orElseGet(() -> loadFromDatabase("user:1"));
IO.println(w + " (lookups=" + lookups + ")");
}
// Output:
// cached:ada (lookups=1)
// cached:ada (lookups=1)
Discussion