What is the difference between listOf and mutableListOf (and the other mutable* factories), and when would you use each?
answer
- mutable* adds add/remove/put
- mutableListOf -> ArrayList; set/map -> LinkedHash*
- default read-only, mutate only when needed
- read-only reference != copy or freeze
- expose List, build with MutableList
basics
~10 slistOf 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 slistOf/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 linesfun 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
Knows mutableListOf lets you add/remove and listOf does not.
States the backing types and the idiom of defaulting to read-only, building with mutable locally.
Explains that a read-only reference is a view not a copy, and the shared-mutation pitfall, suggesting toList() for isolation.
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