Come posso convertire immutable.Map in mutable.Map in Scala?


Risposte:


126

Il modo più pulito sarebbe usare la mutable.Mapfabbrica di varargs. A differenza ++dell'approccio, questo utilizza il CanBuildFrommeccanismo e quindi ha il potenziale per essere più efficiente se il codice della libreria è stato scritto per trarne vantaggio:

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

Questo funziona perché a Mappuò anche essere visto come una sequenza di coppie.


2
Puoi spiegare quale sintassi usi nella seconda riga quando passi il parametro? Cosa fa il colon?
Heinzi

7
: _*è molto simile all'ascrizione del tipo, che dice al compilatore esattamente quale tipo assegnare a una data espressione. Puoi immaginarlo qui come se dicesse "prendi questa sequenza e trattala come un numero di parametri vararg".
Kevin Wright

16
C'è qualcosa che non va nelle biblioteche delle collezioni se questa è la più pulita;)
matanster

2
@matt Potrebbe essere un po 'più corto con importazioni alias, ma tieni presente che sacrificare l'immutabilità è molto poco idiomatico per Scala, non esattamente il genere di cose che mi piacerebbe facendolo sembrare ancora più semplice ... Per curiosità , in quale altro modo potresti proporre di farlo in modo più pulito, se non tramite una copia?
Kevin Wright

Questo è il mio punto, non posso, ma una migliore libreria di collezioni potrebbe renderlo possibile, IMHO.
matante l'

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

1
Sai qual è la complessità temporale asintotica di questo? So che Clojure può trasformare qualsiasi delle sue raccolte persistenti in una "transitoria" (cioè mutabile con funzioni di mutazione tipizzate linearmente) e di nuovo in una persistente O(1)gradualmente. Questo sembra essere O(n), anche se ciò dipende ovviamente da quanto sia intelligente l'implementazione di ++.
Jörg W Mittag

1
@ Jörg - Sono abbastanza sicuro che questo lo sia O(n). Nel limite quando cambi tutto, deve esserlo O(n), anche se potresti provare a differire la creazione della nuova copia per risparmiare tempo, oppure raddoppiare i tempi di accesso leggendo i changeset invece della mappa originale. Quale funziona meglio probabilmente dipende dal tuo caso d'uso.
Rex Kerr

1
@Rustem - Le mappe non sono ordinate. Appariranno nell'ordine che preferiscono (con una mappa hash, in genere è l'ordine della chiave hash). In particolare, le mappe immutabili hanno casi speciali per mappe molto piccole che sono diverse dalle mappe mutabili.
Rex Kerr

Le mappe @Rustem non sono ordinate.
Daniel C. Sobral

4

Che ne dici di usare 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)

Si è fresco, ma fondamentalmente fa la stessa cosa mutable.Map#applycon un po 'più boilerplate.
Kevin Wright,

2

A partire Scala 2.13, tramite costruttori di fabbrica applicati con .to(factory):

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

1

Esiste una variante per creare un mutabile vuoto Mapcon valori predefiniti presi dall'immutabile Map. Puoi memorizzare un valore e sovrascrivere il valore predefinito in qualsiasi 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

Avvertenza (vedi il commento di Rex Kerr): Non potrai rimuovere gli elementi provenienti dalla mappa immutabile:

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

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

3
Questo è utile in alcuni casi, ma nota che non puoi rimuovere un elemento nella tua nuova mappa che era presente nella tua mappa predefinita; puoi solo coprire e scoprire le impostazioni predefinite.
Rex Kerr

Bene, questa soluzione è parziale.
Alexander Azarov
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.