108

How can I convert immutable.Map to mutable.Map in Scala so I can update the values in Map?

Brad Solomon
  • 38,521
  • 31
  • 149
  • 235
Łukasz Lew
  • 48,526
  • 41
  • 139
  • 208

6 Answers6

136

The cleanest way would be to use the mutable.Map varargs factory. Unlike the ++ approach, this uses the CanBuildFrom mechanism, and so has the potential to be more efficient if library code was written to take advantage of this:

val m = collection.immutable.Map(1->"one",2->"Two")
val n = collection.mutable.Map(m.toSeq: _*) 

This works because a Map can also be viewed as a sequence of Pairs.

Kevin Wright
  • 49,540
  • 9
  • 105
  • 155
  • 2
    Can you explain, what syntax you use in the second line when passing the parameter? What does the colon do? – Heinzi Jun 01 '12 at 15:29
  • 7
    `: _*` is much like type ascription, telling the compiler exactly what type to asign to a given expression. You can think of it here as saying "take this sequence, and treat it as a number of vararg params." – Kevin Wright Jun 06 '12 at 10:58
  • 19
    There's something wrong with the collection libraries if this is the cleanest ;) – matanster Nov 08 '15 at 20:57
  • 2
    @matt It could be made a bit shorter with aliased imports, but bear in mind that sacrificing immutability is *very* non-idiomatic for Scala, not exactly the sort of thing I'd enourage by making it look even easier... Out of curiosity, how else could you propose doing it more cleanly, if not via a copy? – Kevin Wright Nov 08 '15 at 21:08
  • That is my point, I can't, but a better collections library could make this possible, IMHO. – matanster Nov 08 '15 at 21:12
  • @Kevin Wright it would be cleaner if the mutable map supported construction from immutable map types eg. ```val m = collection.immutable.Map(1->"one",2->"Two")``` ```val n = collection.mutable.Map(m)``` – Tez Sep 09 '22 at 00:27
47
val myImmutableMap = collection.immutable.Map(1->"one",2->"two")
val myMutableMap = collection.mutable.Map() ++ myImmutableMap
Rex Kerr
  • 166,841
  • 26
  • 322
  • 407
  • 1
    Do you know hat the asymptotic time complexity of this is? I know that Clojure can turn any of its persistent collections into a "transient" one (i.e. a mutable one with linearly-typed mutation functions) and back into a persistent one in `O(1)` steps. This *looks* to be `O(n)`, although that depends of course on how clever the implementation of `++` is. – Jörg W Mittag Feb 18 '11 at 15:34
  • 1
    @Jörg - I'm pretty sure this one is `O(n)`. In the limit as you change everything, it must be `O(n)`, though you could try to defer the creation of the new copy to save time, or you double your access times by reading changesets instead of the original map. Which one performs best probably depends on your use case. – Rex Kerr Feb 18 '11 at 16:00
  • 1
    @Rustem - Maps are unordered. They'll appear in whichever order they feel like (with a hash map, it's typically the order of the hash key). In particular, immutable maps have special cases for really tiny maps which are different from mutable maps. – Rex Kerr Feb 18 '11 at 16:47
  • @Rustem Maps are not ordered. – Daniel C. Sobral Feb 18 '11 at 17:08
12

Starting Scala 2.13, via factory builders applied with .to(factory):

Map(1 -> "a", 2 -> "b").to(collection.mutable.Map)
// collection.mutable.Map[Int,String] = HashMap(1 -> "a", 2 -> "b")
Xavier Guihot
  • 54,987
  • 21
  • 291
  • 190
4

How about using collection.breakOut?

import collection.{mutable, immutable, breakOut}
val myImmutableMap = immutable.Map(1->"one",2->"two")
val myMutableMap: mutable.Map[Int, String] = myImmutableMap.map(identity)(breakOut)
ymnk
  • 1,145
  • 7
  • 7
2

There is a variant to create an empty mutable Map that has default values taken from the immutable Map. You may store a value and override the default at any time:

scala> import collection.immutable.{Map => IMap}
//import collection.immutable.{Map=>IMap}

scala> import collection.mutable.HashMap
//import collection.mutable.HashMap

scala> val iMap = IMap(1 -> "one", 2 -> "two")
//iMap: scala.collection.immutable.Map[Int,java.lang.String] = Map((1,one), (2,two))

scala> val mMap = new HashMap[Int,String] {      
     | override def default(key: Int): String = iMap(key)
     | }
//mMap: scala.collection.mutable.HashMap[Int,String] = Map()

scala> mMap(1)
//res0: String = one

scala> mMap(2)
//res1: String = two

scala> mMap(3)
//java.util.NoSuchElementException: key not found: 3
//  at scala.collection.MapLike$class.default(MapLike.scala:223)
//  at scala.collection.immutable.Map$Map2.default(Map.scala:110)
//  at scala.collection.MapLike$class.apply(MapLike.scala:134)
//  at scala.collection.immutable.Map$Map2.apply(Map.scala:110)
//  at $anon$1.default(<console>:9)
//  at $anon$1.default(<console>:8)
//  at scala.collection.MapLike$class.apply(MapLike.scala:134)....

scala> mMap(2) = "three"

scala> mMap(2)          
//res4: String = three

Caveat (see the comment by Rex Kerr): You will not be able to remove the elements coming from the immutable map:

scala> mMap.remove(1)
//res5: Option[String] = None

scala> mMap(1)
//res6: String = one
Alexander Azarov
  • 12,971
  • 2
  • 50
  • 54
2

With scala 2.13, there are two alternatives: the to method of the source map instance, or the from method of the destination map's companion object.

scala> import scala.collection.mutable
import scala.collection.mutable

scala> val immutable = Map(1 -> 'a', 2 -> 'b');
val immutable: scala.collection.immutable.Map[Int,Char] = Map(1 -> a, 2 -> b)

scala> val mutableMap1 = mutable.Map.from(immutable)
val mutableMap1: scala.collection.mutable.Map[Int,Char] = HashMap(1 -> a, 2 -> b)

scala> val mutableMap2 = immutable.to(mutable.Map)
val mutableMap2: scala.collection.mutable.Map[Int,Char] = HashMap(1 -> a, 2 -> b)

As you can see, the mutable.Map implementation was decided by the library. If you want to choose a particular implementation, for example mutable.HashMap, replace all occurrences of mutable.Map with mutable.HashMap.

Readren
  • 994
  • 1
  • 10
  • 18