Skip to main content

Kotlin Immutable Collections: Kotlin Collections are read-only not immutable

·3 mins
Kotlin
Cover by Author, Logo from Jetbrains

There are some collection types in Kotlin standard library like List, Set and Map. They are all interface. For example, the List is implemented like this:

interface List<out E> : Collection<E> {
    override val size: Int
    override fun isEmpty(): Boolean
    override fun contains(element: @UnsafeVariance E): Boolean
    override fun iterator(): Iterator<E>
    override fun containsAll(elements: Collection<@UnsafeVariance E>): Boolean
    operator fun get(index: Int): E
    fun indexOf(element: @UnsafeVariance E): Int
    fun lastIndexOf(element: @UnsafeVariance E): Int
    fun listIterator(): ListIterator<E>
    fun listIterator(index: Int): ListIterator<E>
    fun subList(fromIndex: Int, toIndex: Int): List<E>
}

List doesn’t have any mutating function like add or remove. Mutating functions are included in MutableList that extends the List interface.

Does Kotlin standard collections actually immutable? #

Actually, Kotlin standard collections are not immutable, but they are read only. Let’s check following code:

suspend fun main() = coroutineScope {
    val numStrings = mutableListOf<String>()
    launch {
        printListEverySeconds(numStrings, 1000)
    }
    delay(500)
    for (i in 1..1000) {
        println("add $i to list")
        numStrings.add(i.toString())
        delay(1000)
    }
}

private suspend fun <T> printListEverySeconds(list: List<T>, times: Int) {
    repeat(times) {
        println("List=$list, size=${list.size}")
        delay(1000)
    }
}

Firstly, it creates a MutableList named numStrings. And it prints list elements every second using printListEverySeconds. The printListEverySeconds function have list parameter that is typed as List. Meanwhile, it adds a new element to numStrings every second. Let’s expect the result.

List=[], size=0
add 1 to list
List=[1], size=1
add 2 to list
List=[1, 2], size=2
add 3 to list
List=[1, 2, 3], size=3
add 4 to list
List=[1, 2, 3, 4], size=4
add 5 to list
List=[1, 2, 3, 4, 5], size=5
...

The result will look like this.

printListEverySeconds takes a list as List type, but actual implementation is MutableList. printListEverySeconds can’t change list elements but the main function can. Because printListEverySeconds use the list as a read-only type List, but main function use the same list instance as MutableList type.

So, the standard collection types are read only, not immutable.

The Kotlinx Collections Immutable library #

Kotlin Organization is developing kotlinx.collections.immutable library as an open source. This library contains actual immutable collections for Kotlin.

There introduce 2 types of collection:

  • Immutable: They specify by their contract the real immutability of their implementors.
  • Persistent: They extend immutable collections and provide efficient modification operations that return new instances of persistent collections with the modification applied. The returned collections can share parts of data structure with the original persistent collections.

Both Immutable and Persistent extend standard collection types. But implementation is immutable.

How to use immutable collections #

Creating a new immutable collection #

val list = persistentListOf(1, 2, 3, 4, 5)

This library contains convenience functions like standard library. you can just make a new instance with persistent prefixed functions.

Note that there are only persistent prefixed functions. immutable prefixed functions (like immutableListOf or immutableSetOf) are deprecated.

Convert collections to immutable collections #

val immutableList = list.toImmutableList()
val persistentList = list.toPersistentList()

All collection types can convert to immutable collections with toImmutable and toPersistent.

Modifying immutable collection #

val list1 = persistentListOf(1, 2, 3, 4, 5)
val list2 = list1.add(6)
println("list1=$list1") // list1=[1, 2, 3, 4, 5]
println("list2=$list2") // list2=[1, 2, 3, 4, 5, 6]

Persistent collections have modification operators. Unlike mutable collections of standard library, they return a new instance collection with the modification applied.

Use immutable collections as standard collection type #

kotlinx.collections.immutable library collections are extends standard library collection types. So, all immutable collections can be considered as standard collection type like List.

Conclusion #

In this article, we understood that the Kotlin standard library collections are not immutable but read only. And we can use kotlinx.collections.immutable library for immutable collections.

Standard collections are not actually immutable. However, read only is useful enough to reduce side effects. You can consider immutable collection for perfect immutability. And also immutable collections for Jetpack Compose is great. Because Jetpack Compose Compiler thinks immutable collections as stable type. (Standard collection types are unstable.)