¿Cómo puedo convertir immutable.Map a mutable.Map en Scala?

93

¿Cómo puedo convertir immutable.Mapa mutable.Mapen Scala para poder actualizar los valores en Map?

Łukasz Lew
fuente

Respuestas:

126

La forma más limpia sería utilizar la mutable.Mapfábrica de varargs. A diferencia del ++enfoque, este usa el CanBuildFrommecanismo y, por lo tanto, tiene el potencial de ser más eficiente si el código de la biblioteca se escribió para aprovechar esto:

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

Esto funciona porque a Maptambién se puede ver como una secuencia de pares.

Kevin Wright
fuente
2
¿Puede explicar qué sintaxis usa en la segunda línea al pasar el parámetro? ¿Qué hace el colon?
Heinzi
7
: _*es muy parecido a la adscripción de tipos, que le dice al compilador exactamente qué tipo asignar a una expresión dada. Puede pensar en ello aquí como si dijera "toma esta secuencia y trátala como varios parámetros de vararg".
Kevin Wright
16
Hay algo mal con las bibliotecas de colección si esta es la más limpia;)
matanster
2
@matt Podría acortarse un poco con las importaciones con alias, pero tenga en cuenta que sacrificar la inmutabilidad es muy poco idiomático para Scala, no es exactamente el tipo de cosas que animaría al hacer que pareciera aún más fácil ... Por curiosidad , ¿de qué otra manera podría proponerse hacerlo de manera más limpia, si no a través de una copia?
Kevin Wright
Ese es mi punto, no puedo, pero una mejor biblioteca de colecciones podría hacer esto posible, en mi humilde opinión.
matanster
41
val myImmutableMap = collection.immutable.Map(1->"one",2->"two")
val myMutableMap = collection.mutable.Map() ++ myImmutableMap
Rex Kerr
fuente
1
¿Sabes cuál es la complejidad temporal asintótica de esto? Sé que Clojure puede convertir cualquiera de sus colecciones persistentes en una "transitoria" (es decir, una mutable con funciones de mutación de tipo lineal) y volver a una persistente en O(1)pasos. Esto parece ser O(n), aunque eso depende, por supuesto, de cuán inteligente sea la implementación de ++.
Jörg W Mittag
1
@ Jörg - Estoy bastante seguro de que este lo es O(n). En el límite a medida que cambia todo, debe estarlo O(n), aunque podría intentar aplazar la creación de la nueva copia para ahorrar tiempo, o duplicar sus tiempos de acceso leyendo conjuntos de cambios en lugar del mapa original. Cuál funciona mejor probablemente depende de su caso de uso.
Rex Kerr
1
@Rustem: los mapas están desordenados. Aparecerán en el orden que deseen (con un mapa hash, normalmente es el orden de la clave hash). En particular, los mapas inmutables tienen casos especiales para mapas realmente pequeños que son diferentes de los mapas mutables.
Rex Kerr
@Rustem Maps no se ordenan.
Daniel C. Sobral
4

¿Qué tal usar 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
fuente
Que es fresco, pero básicamente hace lo mismo que mutable.Map#applycon un poco más repetitivo.
Kevin Wright
4

Comenzando Scala 2.13, a través de constructores de fábrica aplicados con .to(factory):

Map(1 -> "a", 2 -> "b").to(collection.mutable.Map)
// collection.mutable.Map[Int,String] = HashMap(1 -> "a", 2 -> "b")
Xavier Guihot
fuente
1

Existe una variante para crear un mutable vacío Mapque tiene valores predeterminados tomados del inmutable Map. Puede almacenar un valor y anular el predeterminado en cualquier momento:

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

Advertencia (vea el comentario de Rex Kerr): No podrá eliminar los elementos provenientes del mapa inmutable:

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

scala> mMap(1)
//res6: String = one
Alejandro Azarov
fuente
3
Esto es útil en algunos casos, pero tenga en cuenta que no puede eliminar un elemento en su nuevo mapa que estaba presente en su mapa predeterminado; solo puede cubrir y descubrir los valores predeterminados.
Rex Kerr
Bien, esta solución es parcial.
Alexander Azarov