How do getOrDefault and putIfAbsent simplify common Map access patterns, and how do they differ?
answer
- getOrDefault = read-only fallback, no insert
- putIfAbsent = insert only if missing, returns previous
- null mapping treated like absent
- putIfAbsent evaluates its value arg eagerly
- expensive default -> computeIfAbsent instead
basics
~20 sgetOrDefault(key, fallback) returns the value if the key exists, otherwise the fallback — without changing the map. putIfAbsent(key, value) only stores the value if the key is missing (or maps to null) and returns the previous value, leaving an existing value untouched.
solid answer
~40 sBoth replace verbose null-checking boilerplate. `getOrDefault(k, d)` is a pure read: it returns the mapped value if present, else `d`, and never modifies the map — handy for read-time defaults like counters you don't want to insert. `putIfAbsent(k, v)` is a conditional write: it inserts `v` only when `k` is absent or currently mapped to null, and returns the *previous* value (null if it inserted). A key subtlety: both treat a key explicitly mapped to null like an absent key, so getOrDefault returns the default and putIfAbsent will overwrite the null. Also, `putIfAbsent` always evaluates its `v` argument eagerly even when the key is present — if computing `v` is expensive or has side effects, prefer `computeIfAbsent`, which only invokes its mapping function on a miss.
go deeper
Knows getOrDefault returns a fallback without inserting and putIfAbsent inserts only when missing.
Explains the return values and the eager-vs-lazy difference between putIfAbsent and computeIfAbsent.
Articulates the null-as-absent contract and which implementations (HashMap vs ConcurrentHashMap) permit null.
Chooses the right primitive for correctness and cost across single-threaded and concurrent maps, and reasons about side-effect timing.
### The problem these solve Before Java 8, reading a map with a fallback meant: `V v = map.get(k); if (v == null) v = fallback;`. And "insert only if missing" meant: `if (!map.containsKey(k)) map.put(k, v);`. These default methods on the `Map` interface fold that boilerplate into one call. ### getOrDefault(key, defaultValue) Returns the value mapped to `key` **if the key is present**, otherwise returns `defaultValue`. Crucially it is a **pure read**: the map is *not* modified — no entry is created. Example, counting without polluting the map: ```java int count = counts.getOrDefault(word, 0); ``` This returns 0 for an unseen word but does **not** store `word -> 0`. ### putIfAbsent(key, value) A **conditional write**. If `key` is absent (or currently mapped to `null`), it stores `value` and returns `null`. If `key` already has a non-null value, it **does nothing** and returns the **existing** value. So the return value tells you what was there before: ```java Value prev = map.putIfAbsent(k, newValue); // prev == null means we inserted ``` ### The null subtlety (important) In the `Map` contract, a key *explicitly mapped to null* is treated the same as an *absent* key by these methods. So: - `getOrDefault(k, d)` returns `d` if `k` maps to null. - `putIfAbsent(k, v)` will **overwrite** a null mapping with `v`. This matters mainly for `HashMap` (which permits null values); maps like `ConcurrentHashMap` forbid null entirely. ### Eager argument evaluation `putIfAbsent(k, expensive())` always **computes `expensive()`** before the call, even if `k` is present and the value is thrown away — because Java evaluates arguments before invoking the method. If the value is costly to build, or building it should happen *only* on a miss, use `computeIfAbsent(k, key -> expensive())` instead, which is **lazy**: it runs the function only when the key is absent. ### When to use which - Need a fallback but don't want to insert: **getOrDefault**. - Want to insert a default and keep any existing value: **putIfAbsent**. - Insertion default is expensive or you need the key to build it: **computeIfAbsent**.
- Does getOrDefault(k, 0) add k->0 to the map?No. getOrDefault is a pure read; it returns the default but never modifies the map.
- Why might putIfAbsent be wrong when the default is costly to build?Its value argument is evaluated eagerly every call, even on a hit; computeIfAbsent runs the supplier only on a miss, so it avoids the wasted work.
saying these in an interview costs you the question
- Saying getOrDefault inserts the default into the map
- Thinking putIfAbsent returns the new value (it returns the previous)
- Ignoring that a null-mapped key counts as absent
- Using putIfAbsent with an expensive value argument