skip to content

What is the difference between listOf and mutableListOf (and the other mutable* factories), and when would you use each?

level: middleimportance: must knowfreq 75%

answer

  1. mutable* adds add/remove/put
  2. mutableListOf -> ArrayList; set/map -> LinkedHash*
  3. default read-only, mutate only when needed
  4. read-only reference != copy or freeze
  5. expose List, build with MutableList

basics

~10 s

listOf gives a read-only list; mutableListOf gives one you can add to and remove from. Use mutable only when you actually need to change the collection after creating it.

solid answer

~40 s

listOf/setOf/mapOf return the read-only interfaces (List, Set, Map) with no mutators. mutableListOf/mutableSetOf/mutableMapOf return the MutableList/MutableSet/MutableMap interfaces, which add operations like add, remove, set/put, and clear. Concretely, mutableListOf is backed by ArrayList, mutableSetOf by LinkedHashSet, and mutableMapOf by LinkedHashMap. The idiomatic guidance is to prefer the read-only factories by default and reach for the mutable variants only when you genuinely build up or modify the collection. You can also declare a variable as the read-only type while assigning a mutable instance, but the usual pattern is: build with a mutable factory locally, then expose it as the read-only interface. Note these are still references to a single underlying object — a read-only declared reference does not copy or freeze the data.

code

kotlin · 9 lines
kotlin
fun loadNames(): List<String> {
    val acc = mutableListOf<String>()   // build mutably
    for (i in 1..3) acc.add("name$i")
    return acc                           // exposed as read-only List
}

val m = mutableMapOf("a" to 1)
m["b"] = 2          // put via operator
m.remove("a")

go deeper

for a junior

Knows mutableListOf lets you add/remove and listOf does not.

for a middle

States the backing types and the idiom of defaulting to read-only, building with mutable locally.

for a senior

Explains that a read-only reference is a view not a copy, and the shared-mutation pitfall, suggesting toList() for isolation.

for a principal

Reasons about exposing read-only interfaces at module boundaries to protect invariants and avoid leaking mutable internals.

## Two families of factories Kotlin separates **read-only** and **mutable** collection construction at the factory level: | Read-only | Mutable | Backing type | |-----------|---------|--------------| | `listOf` | `mutableListOf` | `ArrayList` | | `setOf` | `mutableSetOf` | `LinkedHashSet` | | `mapOf` | `mutableMapOf` | `LinkedHashMap` | ## What mutable adds The `mutable*` factories return the **`MutableList` / `MutableSet` / `MutableMap`** interfaces. These extend the read-only interfaces and add structural mutators: - List: `add`, `removeAt`, `set` (and `list[i] = x` via the indexed-set operator). - Set: `add`, `remove`, `clear`. - Map: `put` (and `map[k] = v`), `remove`, `getOrPut`. ```kotlin val ro = listOf(1, 2, 3) // List<Int> — no add val mut = mutableListOf(1, 2, 3) // MutableList<Int> mut.add(4) // OK mut[0] = 9 // OK (operator set) // ro.add(4) // does NOT compile ``` ## When to use which - **Default to read-only.** It documents intent and prevents accidental mutation, which matters across function boundaries. - **Use mutable** when you incrementally build or update: a loop that `add`s, an accumulator, a cache. ## Read-only is a view, not a copy Assigning a `MutableList` to a `List`-typed reference does **not** copy or freeze anything. The single underlying object can still be mutated through any reference that still sees it as mutable: ```kotlin val m = mutableListOf(1, 2) val r: List<Int> = m // same object m.add(3) println(r) // [1, 2, 3] — r sees the change ``` For a true defensive copy, build a new collection (e.g. `listOf(*m.toTypedArray())` or `m.toList()`). ## Idiomatic pattern Build locally with a mutable factory, then return/expose the read-only type so callers cannot mutate your internal state.

  • Does returning a MutableList as List protect callers from mutating it?
    Only from the read-only reference. If anyone still holds the original MutableList reference, they can mutate the shared object. For real isolation, return a copy with toList().
  • What concrete class backs mutableSetOf?
    LinkedHashSet, so insertion order is preserved, unlike a plain HashSet.

Read-only is a glass display case (you can look, the structure is fixed for you); mutable is the open shelf where you add and remove items.

saying these in an interview costs you the question

  • Saying assigning to a List type makes a copy
  • Believing a List reference freezes the underlying object
  • Defaulting to mutable everywhere without justification
  • Claiming mutableSetOf is a HashSet with no order
  • Confusing read-only (interface) with deep immutability

context