Compare getOrDefault, getOrElse, and getOrPut on a Kotlin Map. When does each evaluate its fallback, and which one mutates the map?
answer
- getOrDefault = eager value, no mutate
- getOrElse = lazy lambda, no mutate
- getOrPut = lazy lambda, inserts
- getOrPut for grouping/memoize
- stored null makes getOrPut re-run
basics
~20 sAll three give a value when a key is missing. getOrDefault takes a ready value. getOrElse runs a function only when needed. getOrPut also runs a function when needed but stores the result back into the map.
solid answer
~40 sgetOrDefault(key, default) returns the mapped value or the supplied default; the default is an already-evaluated argument (eagerly computed). getOrElse(key) { ... } takes a lambda evaluated lazily only on a miss — cheaper when the fallback is expensive — and does not mutate. getOrPut(key) { ... } is on MutableMap: on a miss it runs the lambda, inserts the result under key, and returns it; on a hit it returns the existing value without calling the lambda. getOrPut is the idiomatic memoize/accumulate primitive (e.g., grouping into a Map<K, MutableList<V>>). Watch the nullable-value edge: getOrPut treats a stored null as absent and will re-run the producer. getOrDefault came from Java's Map; getOrElse/getOrPut are Kotlin stdlib extensions.
code
kotlin · 3 linesval cache = mutableMapOf<Int, Int>()
fun fib(n: Int): Int = if (n < 2) n
else cache.getOrPut(n) { fib(n - 1) + fib(n - 2) }go deeper
Knows all three provide a fallback for missing keys.
Distinguishes eager getOrDefault from lazy getOrElse and knows getOrPut inserts; uses getOrPut for grouping.
Articulates the nullable-value re-run gotcha and chooses based on cost/mutation; knows getOrDefault is the Java inheritance.
Weighs mutation/thread-safety implications (getOrPut is not atomic on a plain map) and suggests computeIfAbsent/concurrent maps when needed.
## The three accessors ```kotlin val m = mutableMapOf("a" to 1) m.getOrDefault("x", 0) // 0 — default is an eager argument m.getOrElse("x") { compute() } // computes only on miss; no mutation m.getOrPut("x") { compute() } // computes on miss, INSERTS, returns it ``` ### getOrDefault(key, defaultValue) Returns the value for `key`, or `defaultValue` if absent. The default is a **normal argument**, so it is **evaluated eagerly** even when the key exists. Inherited from `java.util.Map`. Non-mutating. ### getOrElse(key) { defaultValue } A stdlib extension taking a **lambda** evaluated **lazily** — only when the key is missing. Better when the fallback is costly or has side effects you want to avoid on a hit. Non-mutating. ### getOrPut(key) { defaultValue } Defined on `MutableMap`. On a **miss**: runs the lambda, **`put`s** the result, returns it. On a **hit**: returns the existing value, lambda not called. This is the canonical pattern for lazy initialization / accumulation: ```kotlin val groups = mutableMapOf<Char, MutableList<String>>() for (w in words) { groups.getOrPut(w.first()) { mutableListOf() }.add(w) } ``` ## Lazy vs eager ```kotlin m.getOrDefault("a", expensive()) // expensive() ALWAYS runs m.getOrElse("a") { expensive() } // expensive() runs only on miss ``` ## Nullable-value gotcha `getOrPut` checks the result of `get`. If a key maps to `null`, `get` returns `null`, so `getOrPut` considers it absent and **re-runs the producer** (and re-inserts). For `Map<K, V?>` use `containsKey` logic instead. ## Summary | API | Fallback form | Evaluated | Mutates | |-----|---------------|-----------|---------| | getOrDefault | value | eager | no | | getOrElse | lambda | lazy | no | | getOrPut | lambda | lazy | yes (on miss) |
- Why might getOrElse be preferable to getOrDefault?getOrElse evaluates its lambda lazily, so an expensive or side-effecting fallback runs only on a real miss; getOrDefault evaluates its default argument eagerly every call.
- What surprising behavior does getOrPut have with a null-valued key?It treats a stored null as 'absent' and re-runs the producer lambda, re-inserting a value, because it relies on get() returning non-null.
getOrDefault always brings a spare key; getOrElse only cuts one if the door is locked; getOrPut cuts one and leaves it in the lock for next time.
saying these in an interview costs you the question
- Saying getOrDefault evaluates its default lazily
- Claiming getOrElse mutates the map
- Not knowing getOrPut inserts the computed value
- Missing the null-value re-run gotcha in getOrPut
- Using getOrPut on an immutable Map (it requires MutableMap)