skip to content

Map & Set Semantics

Kotlin's default map and set are backed by the linked implementations, so iteration follows insertion order, and lookup goes through equals and hashCode. The practical follow-ups are getOrPut and getOrElse, and what happens when you use a mutable key.

part ofKotlinoverview, primer and where to startread it →
on this pageshow

questions

5

What does map[key] return in Kotlin when the key is absent, and how is that different from map.getValue(key)?

level: juniorimportance: must knowfreq 65%

answer

  1. [] -> get -> returns V? (nullable)
  2. getValue -> non-null or NoSuchElementException
  3. absent vs present-null ambiguity
  4. containsKey to disambiguate
  5. map[k]=v desugars to put

basics

~10 s

map[key] gives you null when the key isn't there, so the result type is nullable. map.getValue(key) instead throws an exception if the key is missing, returning a non-null value when it exists.

solid answer

~30 s

The indexing operator `map[key]` desugars to `Map.get`, whose return type is `V?` — a missing key yields `null`, so you must handle nullability. `getValue(key)` returns a non-null `V` but throws `NoSuchElementException` when the key is absent (for default LinkedHashMap-backed maps). A subtle trap: with a map whose value type is itself nullable (`Map<K, V?>`), `map[key] == null` is ambiguous — it could mean 'key absent' or 'key present with null value' — so use `containsKey` or `getOrElse` to disambiguate. Prefer `[]` for optional lookups you intend to null-check, and `getValue`/`getOrDefault`/`getOrElse` when you have a clear policy for missing keys.

go deeper

for a junior

Knows [] returns null for missing keys and getValue throws.

for a middle

Explains the absent-vs-present-null ambiguity and reaches for containsKey/getOrElse.

for a senior

Picks the right accessor by failure policy and knows withDefault changes getValue's behavior; understands []= -> put.

for a principal

Frames lookup choice as an API/error-handling contract decision and reasons about null-safety at module boundaries.

## The indexing operator In Kotlin `map[key]` is **syntactic sugar** for the `get(key)` operator function on `Map`. Its signature is: ```kotlin operator fun get(key: K): V? ``` The `?` means the result is **nullable**. A missing key returns `null`; you cannot distinguish 'absent' from 'present-but-null' by the return value alone when `V` itself is nullable. ## getValue contrasts by throwing ```kotlin val ages = mapOf("ann" to 30) val a: Int? = ages["ann"] // 30 val b: Int? = ages["bob"] // null (no exception) val c: Int = ages.getValue("ann") // 30, non-null val d: Int = ages.getValue("bob") // throws NoSuchElementException ``` `getValue` returns a **non-null** `V` and throws `NoSuchElementException` for an absent key on the standard map. (For custom maps built with `withDefault`, `getValue` returns the default instead of throwing.) ## The nullable-value trap ```kotlin val m: Map<String, Int?> = mapOf("k" to null) println(m["k"]) // null -> but key IS present! println(m.containsKey("k")) // true ``` When value type is nullable, `m[key] == null` does not prove absence. Use `containsKey`, or `getOrElse` with a sentinel, to be precise. ## Choosing - `map[key]` — you'll null-check or chain with `?.`, `?:`. - `getValue` — the key **must** exist; fail loudly otherwise. - `getOrDefault` / `getOrElse` — you have a sensible fallback. The bracket operator also has a mutating sibling for `MutableMap`: `map[key] = value` desugars to `put`.

  • How can you tell 'key absent' apart from 'key present with null value'?
    Use containsKey(key), or getOrElse with a unique sentinel object; the [] result alone can't distinguish them when values are nullable.
  • What does map[key] = value compile to?
    The set-indexing operator desugars to MutableMap.put(key, value), returning the previous value (which [] assignment discards).

saying these in an interview costs you the question

  • Claiming map[key] throws when the key is missing
  • Saying getValue returns null for absent keys
  • Ignoring the present-but-null ambiguity
  • Forgetting that [] returns a nullable type

context

open as a page

When you call mapOf(...) or setOf(...) in Kotlin, what concrete JVM type backs the result, and what does that mean for iteration order?

level: juniorimportance: must knowfreq 70%

basics

~10 s

Kotlin's default maps and sets keep elements in the order you inserted them. They are backed by LinkedHashMap and LinkedHashSet, so looping over them gives you items in insertion order.

open as a page

Compare getOrDefault, getOrElse, and getOrPut on a Kotlin Map. When does each evaluate its fallback, and which one mutates the map?

level: middleimportance: must knowfreq 60%

basics

~20 s

All 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.

open as a page

What contract must an element type satisfy for a Kotlin Set (or Map key) to deduplicate correctly, and what breaks if you use a mutable object as a key?

level: seniorimportance: must knowfreq 55%

basics

~20 s

Sets and map keys decide 'same element' using equals() and hashCode(). If two objects are equal they must have the same hashCode. If you change a key after inserting it, the set can no longer find it.

open as a page

Is getOrPut on a plain mutableMapOf safe under concurrent access, and what is the correct alternative if you need atomic lazy initialization?

level: principalimportance: should knowfreq 30%

basics

~10 s

No. getOrPut on a normal map is not thread-safe: two threads can both see a missing key and both compute and store a value. For safe concurrent lazy init use a concurrent map's computeIfAbsent.

open as a page