19

If I have an immutableMap: Map<String, Int> and want to convert it to a mutableMap: MutableMap<String, Int>, what would be the best way to do that? There doesn't seem like any convenient way.

spierce7
  • 14,797
  • 13
  • 65
  • 106

2 Answers2

23

Update: The Kotlin 1.1 stdlib has a .toMutableMap() function. This should be used instead of creating your own.

----------------------- Old Answer Below ------------------------------

The following extension function works by copying the Map to a MutableMap.

fun <K, V> Map<K, V>.toMutableMap(): MutableMap<K, V> {
  return HashMap(this)
}

Some of the other answers suggest checking to see if the map is a MutableMap and then casting to it if it is. This should be avoided at all costs, as it's a bad practice. The fact that it was downcasted to a Map in Kotlin implies that it's expected that this Map shouldn't be changed, and it would be dangerous to do so.

spierce7
  • 14,797
  • 13
  • 65
  • 106
7

First, as covered in previous questions about Kotlin read only collections and their immutability, they are really read only live views of likely mutable collections.

Kotlin CAN safely check the type for mutability for these since there are markers on the interfaces that let Kotlin know, for example:

// assuming some variable readOnlyMap of type Map<String, String>

val mutableMap: MutableMap<String, String> = if (readOnlyMap is MutableMap<*,*>) {
    readOnlyMap as MutableMap
} else {
    HashMap(readOnlyMap)
}

This is check internally calls TypeIntrinsics.isMutableMap and is a valid way to know if it is castable. There is no danger in the cast if you do this check first. You'll note the compiler is perfectly happy and without warnings. This could be dangerous if you don't intend to modify the same map being treated as read-only, but shows that you can cast for affect.

If you do not want the chance of modifying your original map, then of course always make a copy by simply calling constructor of HashMap with another Map, HashMap(readOnlyMap). Or make an extension function:

    fun <K, V> Map<K, V>.toMutableCopy() = HashMap(this)

and call it:

    val mutableMap = readOnlyMap.toMutableCopy()

And now you have your simple way.

See also:

Community
  • 1
  • 1
Jayson Minard
  • 84,842
  • 38
  • 184
  • 227
  • I think you should specify the generics of the HashMap explicitly, otherwise you end up with a map with platform key/value types. – Christoph Leiter Jul 27 '16 at 07:35
  • 3
    I would advise against the `is MutableMap` check as it can return `true` for Java collections that aren't actually mutable. For example try `Collections.unmodifiableMap(mutableMapOf("a" to "b")) is MutableMap<*,*>`. – Kirill Rakhman Jul 27 '16 at 08:57
  • This looks like the best answer to me. One thing you might add is an example which does not use `HashMap` directly (I believe `mutableMapOf` actually uses `LinkedHashMap`). e.g.: `val mutableMap = map.entries.associateTo(mutableMapOf()) { entry -> entry.toPair() }`. – mfulton26 Jul 27 '16 at 14:32
  • 2
    Casting the map in some situations, and copying it in others can lead to some unpredictable behavior. I'm not sure that should be encouraged in your answer. – spierce7 Jul 27 '16 at 20:39
  • a lot depends on he full use case which isn't known, so I gave various options. Buyer beware. – Jayson Minard Jul 28 '16 at 01:50
  • @KirillRakhman true, the MutableMap interface does not guarantee that every implementation is mutable. Again, depends on how general or specific the case is what is required to make it rock solid. – Jayson Minard Jul 28 '16 at 01:54
  • Note that the first one can be represented more succinctly as `readOnlyMap as? MutableMap ?: HashMap(readOnlyMap)` by utilizing the safe cast and elvis operators. – CAD97 Nov 30 '17 at 21:47