In Kotlin, what is the difference between List and MutableList (and Set/MutableSet, Map/MutableMap)?
answer
- Pairs: List/MutableList, Set/MutableSet, Map/MutableMap
- Read-only = no add/set/remove declared
- MutableX extends X and adds mutators
- [] is get; []= is set (only on mutable)
- Compile-time only; not true immutability
basics
~10 sList only lets you read items. MutableList lets you also add, remove, or change them. The same idea applies to Set/MutableSet and Map/MutableMap.
solid answer
~40 sKotlin's standard library splits each collection into a read-only interface and a mutable one. List, Set, and Map expose only read operations: size, get/[], contains, iterator, and read-style functions. The MutableX interfaces extend their read-only parent and add mutators: MutableList.add/remove/set, MutableSet.add/remove, MutableMap.put/remove and []= via set. There is no separate immutable interface name — the read-only one IS List. So MutableList is a List, but a List variable forbids calling add at compile time. You pick the type at the variable/parameter/return position to express intent: declare List<T> when callers should not modify, MutableList<T> only when mutation is part of the contract. This is purely a compile-time, interface-level distinction, not a guarantee that the underlying object can never change.
code
kotlin · 6 linesfun sum(xs: List<Int>): Int = xs.sum() // promises not to mutate
fun fill(xs: MutableList<Int>) { xs.add(0) } // mutation is part of contract
val m = mutableListOf(1, 2)
fill(m) // MutableList is a List, also accepted by sum
println(sum(m)) // 3go deeper
Knows List is read-only and MutableList lets you add/remove; can pick the right one for a variable.
Articulates that MutableList extends List and that the choice expresses intent in APIs.
Distinguishes read-only from immutable and explains the compile-time-only nature plus aliasing risks.
Frames the split as an API-design tool for least-privilege contracts and discusses how it interacts with platform/Java collections.
## The interface split Kotlin's collections live in `kotlin.collections` and come in **pairs**: - `List<out E>` / `MutableList<E>` - `Set<out E>` / `MutableSet<E>` - `Map<K, out V>` / `MutableMap<K, V>` - plus `Collection<out E>` / `MutableCollection<E>` and `Iterable`/`MutableIterable` as parents. The **read-only** interface (e.g. `List`) declares only operations that *observe* the collection: `size`, `isEmpty()`, `contains(e)`, `get(index)` (the `[]` operator), `indexOf`, `iterator()`, plus the many extension functions (`map`, `filter`, `first`…) that return new collections. The **mutable** interface (e.g. `MutableList`) **extends** the read-only one and adds *mutators*: - `MutableList<E>`: `add`, `remove`, `set` (the `[]=` operator), `removeAt`, `clear`, plus a `MutableListIterator`. - `MutableSet<E>`: `add`, `remove`, `addAll`, `clear`. - `MutableMap<K,V>`: `put` (the `[]=` operator), `remove`, `clear`, and `entries` of type `MutableMap.MutableEntry`. ```kotlin val read: List<Int> = listOf(1, 2, 3) // read.add(4) // compile error: List has no add val mutable: MutableList<Int> = mutableListOf(1, 2, 3) mutable.add(4) // OK mutable[0] = 99 // OK, uses set() ``` ## Why it matters The split lets you **express intent in the type**. A function that takes `List<T>` promises not to mutate the argument; a function returning `List<T>` tells callers "don't try to change this." You only widen to `MutableList<T>` when mutation is genuinely part of the contract. ## Read-only is not immutable This is a **compile-time** distinction. `List` simply doesn't *declare* mutators — it does **not** guarantee the object never changes. The same object can be referenced through both a `MutableList` and a `List` variable; mutating via the mutable reference is visible through the read-only one. True immutability is a separate concern (covered by read-only *views* vs truly immutable structures). ## Subtyping Because `MutableList` extends `List`, every `MutableList` *is* a `List`, so you can pass a `MutableList` where a `List` is expected — the callee just sees fewer operations.
- Can you pass a MutableList to a function expecting a List?Yes. MutableList extends List, so it is a subtype; the function just can't call mutators.
- Does declaring a parameter as List make the collection immutable?No. It only hides mutators at compile time; the underlying object may still be mutated elsewhere.
A read-only List is a museum exhibit you can look at; a MutableList is the same display but with a sign saying 'staff may rearrange'.
saying these in an interview costs you the question
- Claiming List is 'immutable' and can never change
- Thinking MutableList and List are unrelated types rather than subtype/supertype
- Saying you cannot pass a MutableList where a List is required
- Confusing read-only interface with Java's Collections.unmodifiableList semantics by name only