如何将 immutable.Map 转换为 mutable.Map 在 Scala 中?

如何在 Scala 中将 immutable.Map转换为 mutable.Map,以便更新 Map中的值?

60190 次浏览
val myImmutableMap = collection.immutable.Map(1->"one",2->"two")
val myMutableMap = collection.mutable.Map() ++ myImmutableMap

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)

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

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.

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")

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.