Comment puis-je convertir immutable.Map en mutable.Map dans Scala?

93

Comment puis - je convertir immutable.Mapà mutable.MapScala afin que je puisse mettre à jour les valeurs Map?

Łukasz Lew
la source

Réponses:

126

Le moyen le plus propre serait d'utiliser l' mutable.Mapusine varargs. Contrairement à l' ++approche, cela utilise le CanBuildFrommécanisme, et a donc le potentiel d'être plus efficace si le code de la bibliothèque a été écrit pour en tirer parti:

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

Cela fonctionne car a Mappeut également être considéré comme une séquence de paires.

Kevin Wright
la source
2
Pouvez-vous expliquer, quelle syntaxe vous utilisez dans la deuxième ligne lors du passage du paramètre? Que fait le côlon?
Heinzi
7
: _*est un peu comme l'attribution de type, indiquant au compilateur exactement quel type attribuer à une expression donnée. Vous pouvez le considérer ici comme disant "prenez cette séquence et traitez-la comme un certain nombre de paramètres vararg".
Kevin Wright
16
Il y a quelque chose qui ne va pas avec les bibliothèques de collection si c'est le plus propre;)
matanster
2
@matt Cela pourrait être un peu raccourci avec des importations aliasées, mais gardez à l'esprit que sacrifier l'immuabilité est très non idiomatique pour Scala, pas exactement le genre de chose que j'encouragerais en le rendant encore plus facile ... Par curiosité , comment pourriez-vous proposer de le faire plus proprement, sinon via une copie?
Kevin Wright
C'est mon point, je ne peux pas, mais une meilleure bibliothèque de collections pourrait rendre cela possible, à mon humble avis.
matanster
41
val myImmutableMap = collection.immutable.Map(1->"one",2->"two")
val myMutableMap = collection.mutable.Map() ++ myImmutableMap
Rex Kerr
la source
1
Savez-vous quelle est la complexité temporelle asymptotique de cela? Je sais que Clojure peut transformer n'importe laquelle de ses collections persistantes en une collection "transitoire" (c'est-à-dire une mutable avec des fonctions de mutation linéairement typées) et revenir en une persistante par O(1)étapes. Cela semble être le cas O(n), bien que cela dépende bien sûr de l'intelligence de la mise en œuvre ++.
Jörg W Mittag
1
@ Jörg - Je suis presque sûr que celui-ci l'est O(n). Dans la limite lorsque vous modifiez tout, cela doit être O(n), bien que vous puissiez essayer de différer la création de la nouvelle copie pour gagner du temps, ou vous doublez vos temps d'accès en lisant les changesets au lieu de la carte d'origine. Celui qui fonctionne le mieux dépend probablement de votre cas d'utilisation.
Rex Kerr
1
@Rustem - Les cartes ne sont pas ordonnées. Ils apparaîtront dans l'ordre dans lequel ils se sentent (avec une carte de hachage, c'est généralement l'ordre de la clé de hachage). En particulier, les cartes immuables ont des cas particuliers pour les cartes vraiment minuscules qui sont différentes des cartes mutables.
Rex Kerr
Les cartes @Rustem ne sont pas commandées.
Daniel C.Sobral
4

Que diriez-vous d'utiliser 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
la source
Il est cool, mais fait essentiellement la même chose que mutable.Map#applyd'un peu plus passe- partout.
Kevin Wright
4

Démarrage Scala 2.13, via les constructeurs d'usine appliqué avec .to(factory):

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

Il existe une variante pour créer un mutable vide Mapdont les valeurs par défaut sont extraites de l'immuable Map. Vous pouvez stocker une valeur et remplacer la valeur par défaut à tout moment:

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

Attention (voir le commentaire de Rex Kerr): Vous ne pourrez pas supprimer les éléments provenant de la carte immuable:

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

scala> mMap(1)
//res6: String = one
Alexandre Azarov
la source
3
Ceci est utile dans certains cas, mais notez que vous ne pouvez pas supprimer un élément de votre nouvelle carte qui était présent dans votre carte par défaut; vous ne pouvez couvrir et découvrir les valeurs par défaut.
Rex Kerr
Oui, cette solution est partielle.
Alexander Azarov