DEV Community

Hugo Martins
Hugo Martins

Posted on • Originally published at hugomartins.io on

Understanding Differences Between List and MutableList in Kotlin

I’ve been thinking about immutability a lot…interesting, I know. I can almost feel your excitement through this screen! In particular, I’ve been trying to investigate and learn ways in which you can break immutability in Kotlin, either knowingly or unknowingly. My last attempt involves casting immutable lists as mutable lists, in order to modify a supposedly immutable list into a mutable one. While I have been unsuccessful in my attempts, with the particulars of what I want to do, I learned quite an interesting amount about what makes a List different from a MutableList, and I’ll share a bit more about what I’ve learned here.

Interfaces, Interfaces, Interfaces

A quick glance at Kotlin’s documentation provides a good overview about what the difference between them is: while List provides read-only access to stored elements, MutableList provides “list-specific write operations” that allow us to add or remove particular elements from an existing list. What peaked my interest was the last sentence of the documentation:

In Kotlin, the default implementation of List is ArrayList which you can think of as a resizable array.

Uhh, exciting!! So, List isn’t really a simple list per se but is actually an interface for ArrayList. We can assume that MutableList is simply another interface for ArrayList, I guess. We could assume or we can simply go and check how the Kotlin standard library actually implements these things.

In package kotlin.collections we can see a definition for List:

public interface List<out E> : Collection<E> { (...) }
Enter fullscreen mode Exit fullscreen mode

List inherits from Collection and features a series of function definitions, all of them read-only, such as get. Collection is another interface which inherits from Iterable.

In the same package, we can see a definition for MutableList:

public interface MutableList<E> : List<E>, MutableCollection<E> { (...) }
Enter fullscreen mode Exit fullscreen mode

MutableList inherits from MutableCollection. MutableList features a series of additional methods such as add and remove, granting mutability capacities. MutableCollection inherits from Collection and MutableIterable, which inherit from Iterator.

In itself, this doesn’t let us know much more but it already unveils that List and MutableList are essentially two different interfaces, one with read-only methods and the other with additional write-specific methods.

Functions, Functions, Functions

Let’s look at it from a different perspective. What happens when we invoke List() for example? Invoking List() surprisingly doesn’t initiate an object immediately:

@SinceKotlin("1.1")
@kotlin.internal.InlineOnly
public inline fun <T> List(size: Int, init: (index: Int) -> T): List<T> = MutableList(size, init)
Enter fullscreen mode Exit fullscreen mode

As we can see from the above snippet, when we invoke List() we are actually calling a function that returns a List , the interface we saw above, but it actually invokes another function:

@SinceKotlin("1.1")
@kotlin.internal.InlineOnly
public inline fun <T> MutableList(size: Int, init: (index: Int) -> T): MutableList<T> {
    val list = ArrayList<T>(size)
    repeat(size) { index -> list.add(init(index)) }
    return list
}
Enter fullscreen mode Exit fullscreen mode

MutableList() creates a new ArrayList, initializes it with the appropriate values and then returns. Upon returning, ArrayList is transformed into a MutableList. We can now clearly see why, in Kotlin’s documentation, it appears so explicitly that the default implementation of lists is an ArrayList.

For the curious among you, ArrayList is just a typed alias for java.util.ArrayList:

@SinceKotlin("1.1") public actual typealias ArrayList<E> = java.util.ArrayList<E>
Enter fullscreen mode Exit fullscreen mode

If we go inspect what java.util.ArrayList actually is…it turns out that it extends java.util.function.Consumer.AbstractList and implements java.util.List. We are starting to see some connections here!

Mixing Interfaces and Functions

So, in essence, List and MutableList can be two separate things at once:

  1. Interfaces, where List provides only read-only properties and MutableList provides write operations.
  2. Functions, which can be used to instantiate objects from those interfaces that rely on ArrayList for their actual implementation.

Let’s take another step back now…in Kotlin’s documentation we are usually greeted with listOf and mutableListOf. What are these?

Let’s take a sneak peek at one of the listOf functions:

/**
 * Returns a new read-only list of given elements. The returned list is serializable (JVM).
 * @sample samples.collections.Collections.Lists.readOnlyList
 */
public fun <T> listOf(vararg elements: T): List<T> = if (elements.size > 0) elements.asList() else emptyList()
Enter fullscreen mode Exit fullscreen mode

We know elements from vararg will be arrays, which means that listOf essentially transforms an array of elements into a list with List interface.

mutableListOf is a bit different but in essence the same:

/**
 * Returns a new [MutableList] with the given elements.
 * @sample samples.collections.Collections.Lists.mutableList
 */
public fun <T> mutableListOf(vararg elements: T): MutableList<T> =
    if (elements.size == 0) ArrayList() else ArrayList(ArrayAsCollection(elements, isVarargs = true))
Enter fullscreen mode Exit fullscreen mode

In both cases above, the functions call internal implementations of ArrayList but they return different interfaces which, as we have seen above, provide different mutability functions: while List only offers read-only operations, MutableList offers write operations too.

Maybe I’ve exaggerated the amount of digging into Kotlin’s source code but I believe the differences between List and MutableList became clearer with this exploration, even though some internal magic of how Kotlin works might still be a bit of a mystery.

Latest comments (0)