How to turn a Mutable Collection into an Immutable one

Simply call toMap() on your MutableMap.

val myMap = mutableMapOf<String, String>("x" to "y").toMap()

Done.

The same also works for lists.


As mentioned here and here, you'd need to write your own List implementation for that, or use an existing one (Guava's ImmutableList comes to mind, or Eclipse Collections as Andrew suggested).

Kotlin enforces list (im)mutability by interface only. There are no List implementations that don't also implement MutableList.

Even the idiomatic listOf(1,2,3) ends up calling Kotlin's ArraysUtilJVM.asList() which calls Java's Arrays.asList() which returns a plain old Java ArrayList.

If you care more about protecting your own internal list, than about the immutability itself, you can of course copy the entire collection and return it as an List, just like Kotlin does:

return ArrayList(original)

Use Collections to converts a Mutable list to Immutable list, Example:

Mutable list:

val mutableList = mutableListOf<String>()

Converts to Immutable list:

val immutableList = Collections.unmodifiableList(mutableList)

Currently in Kotlin stdlib there are no implementations of List<T> (Map<K,V>) that would not also implement MutableList<T> (MutableMap<K,V>). However due to Kotlin's delegation feature the implementations become one liners:

class ImmutableList<T>(private val inner:List<T>) : List<T> by inner
class ImmutableMap<K, V>(private val inner: Map<K, V>) : Map<K, V> by inner

You can also enhance the creation of the immutable counterparts with extension methods:

fun <K, V> Map<K, V>.toImmutableMap(): Map<K, V> {
    if (this is ImmutableMap<K, V>) {
        return this
    } else {
        return ImmutableMap(this)
    }
}

fun <T> List<T>.toImmutableList(): List<T> {
    if (this is ImmutableList<T>) {
        return this
    } else {
        return ImmutableList(this)
    }
}

The above prevents a caller from modifying the List (Map) by casting to a different class. However there are still reasons to create a copy of the original container to prevent subtle issues like ConcurrentModificationException:

class ImmutableList<T> private constructor(private val inner: List<T>) : List<T> by inner {
    companion object {
        fun <T> create(inner: List<T>) = if (inner is ImmutableList<T>) {
                inner
            } else {
                ImmutableList(inner.toList())
            }
    }
}

class ImmutableMap<K, V> private constructor(private val inner: Map<K, V>) : Map<K, V> by inner {
    companion object {
        fun <K, V> create(inner: Map<K, V>) = if (inner is ImmutableMap<K, V>) {
            inner
        } else {
            ImmutableMap(hashMapOf(*inner.toList().toTypedArray()))
        }
    }
}

fun <K, V> Map<K, V>.toImmutableMap(): Map<K, V> = ImmutableMap.create(this)
fun <T> List<T>.toImmutableList(): List<T> = ImmutableList.create(this)

While the above is not hard to implement there are already implementations of immutable lists and maps in both Guava and Eclipse-Collections.

Tags:

Kotlin