thecodex.expert · The Codex Family of Knowledge
Kotlin

Collections

A List in Kotlin isn’t actually guaranteed immutable — it’s a read-only VIEW, and the exact same underlying data can still change if another part of the program holds a MutableList reference to it.

Kotlin 2.1 List ≠ truly immutable Last verified:
Canonical Definition

listOf(), setOf(), and mapOf() create read-only collections — List, Set, and Map interfaces exposing no add/remove/put methods at all. mutableListOf(), mutableSetOf(), and mutableMapOf() create their mutable counterparts, which DO expose those methods. The genuinely important nuance: List is a read-only VIEW of the interface, not a deep immutability guarantee — if the same underlying object is also referenced somewhere as a MutableList, changes made through that mutable reference are visible through the read-only List reference too, since they're the same object in memory.

Read-only vs mutable collections

numbers has no add() or remove() method at all — attempting to call one is a compile error, since List simply doesn't expose those methods in its interface.

Kotlinreadonly_vs_mutable.kt
val numbers: List<Int> = listOf(1, 2, 3)     // read-only interface — no add/remove available
// numbers.add(4)                                  // COMPILE ERROR: List has no add() method

val mutableNumbers: MutableList<Int> = mutableListOf(1, 2, 3)
mutableNumbers.add(4)   // fine — MutableList exposes add()
println(mutableNumbers)   // [1, 2, 3, 4]

The real gotcha: List is a view, not a deep guarantee

readOnlyView and actualList refer to the EXACT SAME underlying object — the List reference just doesn't expose mutating methods on itself, but it doesn't stop the object from actually changing through a different, mutable reference to it.

Kotlinview_not_deep_immutable.kt
val actualList = mutableListOf(1, 2, 3)
val readOnlyView: List<Int> = actualList   // same object, viewed through a read-only interface

actualList.add(4)               // mutating through the MUTABLE reference
println(readOnlyView)            // [1, 2, 3, 4] — the "read-only" view SAW the change too!

Common operations: functional-style, not loop-based

map, filter, and sum (among many others) return a NEW collection or value rather than mutating the original — idiomatic Kotlin favors this functional style over manually indexed loops for most collection processing.

Kotlincommon_operations.kt
val numbers = listOf(1, 2, 3, 4, 5)

val doubled = numbers.map { it * 2 }         // [2, 4, 6, 8, 10]
val evens = numbers.filter { it % 2 == 0 }    // [2, 4]
val total = numbers.sum()                       // 15
val names = mapOf("a" to 1, "b" to 2)          // Map, built from pairs directly

Sources

1
JetBrains. Kotlin Documentation, "Collections overview," kotlinlang.org/docs/collections-overview.html.