Read-Only vs Mutable Collections

Two Interfaces for Each Collection

Read-only (default)MutableCreate with
List<T>MutableList<T>listOf / mutableListOf
Set<T>MutableSet<T>setOf / mutableSetOf
Map<K, V>MutableMap<K, V>mapOf / mutableMapOf
val fruits = listOf("Apple", "Mango")
fruits.add("Banana")   // ERROR: Unresolved reference 'add' (List has no add)

val vs Mutable

Example

val / var is about the variable; List / MutableList is about the contents. They are independent.

fun main() {
    val items = mutableListOf("A")   // variable fixed, contents can change
    items.add("B")
    println(items)

    var menu = listOf("Tea")         // contents fixed, variable can point to a new list
    menu = menu + "Coffee"           // + creates a NEW list
    println(menu)

    val readOnly: List<String> = items   // read-only VIEW of the same list
    items.add("C")
    println(readOnly)                    // sees the change!

    val copy = items.toList()            // real independent copy
    items.add("D")
    println(copy)
    println(items)
}

Output

[A, B]
[Tea, Coffee]
[A, B, C]
[A, B, C]
[A, B, C, D]

Tip: Expose List from functions and classes, keep MutableList private. Android ViewModels follow this pattern: a private mutable state and a public read-only one.

Java Comparison

In Java, List has add() even when the list is unmodifiable (it throws at run time). In Kotlin, a read-only List simply has no add(), so the mistake is caught at compile time. Both still use the same java.util.ArrayList, LinkedHashSet and LinkedHashMap classes underneath.