skip to content

In Kotlin, what is the difference between accessing a list element with `list[i]`, `list.getOrNull(i)`, and `list.getOrElse(i) { default }`? When does each fail or succeed?

level: juniorimportance: must knowfreq 78%

answer

  1. [] throws, getOrNull → null, getOrElse → computed default
  2. getOrElse lambda receives the bad index
  3. map[key] is null-safe; getValue throws
  4. Pair getOrNull with ?: Elvis

basics

~10 s

list[i] throws an error if the index is out of range. getOrNull(i) returns null instead of throwing. getOrElse(i) { ... } returns a value you compute when the index is out of range.

solid answer

~40 s

`list[i]` calls `get(index)` and throws `IndexOutOfBoundsException` when the index is negative or `>= size`. `getOrNull(i)` is a safe variant returning `T?` — it gives `null` for any out-of-bounds index instead of throwing. `getOrElse(i) { idx -> ... }` returns a computed fallback (the lambda receives the bad index) and never throws. Use `[]` when the index is provably valid (e.g. from `indices`), `getOrNull` when you want to branch with `?:` or `?.let`, and `getOrElse` when you have a meaningful default. None of these mutate the list. For maps the analogue is `map[key]` (returns `V?`, no throw) versus `getValue(key)` (throws `NoSuchElementException`) and `getOrDefault`/`getOrElse`.

code

kotlin · 5 lines
kotlin
val xs = listOf(10, 20, 30)
val a = xs[2]                       // 30
val b = xs.getOrNull(9)             // null
val c = xs.getOrElse(9) { it * 0 }  // 0 (lambda gets index 9)
val d = xs.getOrNull(9) ?: -1       // -1

go deeper

for a junior

Knows [] throws and getOrNull returns null; can pick the right one for a bounds-uncertain index.

for a middle

Uses getOrElse with the index parameter and composes getOrNull with Elvis/let; knows the map analogues.

for a senior

Explains O(1) vs O(n) for List vs Sequence access and argues against exception-driven control flow.

for a principal

Sets team conventions: prefer total accessors at boundaries, reserve [] for provably-valid indices from indices/lastIndex.

## The three access forms Kotlin's `List` exposes element access through the indexed-access operator and several safe helpers. - **`list[i]`** — sugar for `list.get(i)`. The `get` member is defined on `List<E>` and throws `IndexOutOfBoundsException` when `i < 0` or `i >= size`. Use it only when the index is guaranteed valid. - **`list.getOrNull(i)`** — an extension on `List<T>` returning `T?`. It performs a bounds check (`if (index in indices) get(index) else null`) and returns `null` for any out-of-range index. Great with the Elvis operator: `list.getOrNull(i) ?: fallback`. - **`list.getOrElse(i) { idx -> ... }`** — an extension returning `T`. When in bounds it returns the element; otherwise it invokes the lambda, **passing the requested index**, and returns its result. Never throws. ```kotlin val xs = listOf("a", "b", "c") xs[1] // "b" xs.getOrNull(5) // null (no throw) xs.getOrElse(5) { "?" } // "?" xs.getOrElse(5) { idx -> "missing@$idx" } // "missing@5" ``` ## Why not just catch the exception? Using `getOrNull`/`getOrElse` expresses intent and avoids exception-driven control flow, which is slower and obscures logic. The `?:` and `?.let { }` operators compose cleanly with the nullable result. ## Maps are different For `Map<K, V>`: - `map[key]` returns `V?` and never throws (absent key → `null`). - `map.getValue(key)` throws `NoSuchElementException` if the key is absent. - `map.getOrElse(key) { default }` and `map.getOrDefault(key, default)` supply fallbacks. ## Edge cases - Negative indices are out of bounds for all of these. - `getOrNull`/`getOrElse` are O(1) on `List` (random access) but on a `Sequence` or `Iterable` the analogous `elementAtOrNull` is O(n) because it iterates.

  • What does the lambda passed to getOrElse receive?
    The requested (out-of-bounds) index, so you can build a message or compute an index-dependent default.
  • How do you do the same safe access on a Map?
    Use map[key] (returns V?, never throws) or map.getOrElse(key) { default } / getOrDefault; getValue throws on a missing key.

Like asking for a book by shelf number: [] crashes if the slot is empty, getOrNull hands you nothing, getOrElse hands you a substitute you chose.

saying these in an interview costs you the question

  • Claiming list[i] returns null when out of bounds (it throws)
  • Saying getOrNull throws an exception
  • Wrapping every access in try/catch instead of using the safe accessors
  • Thinking getOrElse's lambda receives the element rather than the index
  • Confusing map.getValue (throws) with map[key] (null-safe)

context