Jak mogę przekonwertować immutable.Map na mutable.Map w Scali?

95

Jak mogę przekonwertować immutable.Mapna mutable.Mapw Scali, aby zaktualizować wartości w Map?

Łukasz Lew
źródło

Odpowiedzi:

131

Najczystszym sposobem byłoby skorzystanie z mutable.Mapfabryki Varargs. W przeciwieństwie do tego ++podejścia, to wykorzystuje CanBuildFrommechanizm, a więc może być bardziej wydajne, jeśli kod biblioteki został napisany w celu wykorzystania tego:

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

To działa, ponieważ a Mapmożna również postrzegać jako sekwencję par.

Kevin Wright
źródło
2
Czy możesz wyjaśnić, jakiej składni używasz w drugim wierszu podczas przekazywania parametru? Co robi okrężnica?
Heinzi
7
: _*jest bardzo podobny do przypisywania typów, mówiąc kompilatorowi dokładnie, jaki typ przypisać do danego wyrażenia. Możesz myśleć o tym tutaj jako o powiedzeniu „weź tę sekwencję i potraktuj ją jako kilka parametrów vararg”.
Kevin Wright
16
Coś jest nie tak z bibliotekami kolekcji, jeśli to jest najczystsze;)
matanster
2
@matt To może być nieco krótszy z importu aliasami, należy jednak pamiętać, że poświęcając niezmienność jest bardzo non-idiomatyczne dla Scala, nie dokładnie coś takiego bym enourage przez co wygląda jeszcze łatwiej ... z ciekawości , jak inaczej mógłbyś zaproponować, aby zrobić to czysto, jeśli nie za pomocą kopii?
Kevin Wright
O to mi chodzi, nie mogę, ale lepsza biblioteka zbiorów mogłaby to umożliwić, IMHO.
matanster
42
val myImmutableMap = collection.immutable.Map(1->"one",2->"two")
val myMutableMap = collection.mutable.Map() ++ myImmutableMap
Rex Kerr
źródło
1
Czy wiesz, jaka jest asymptotyczna złożoność czasu? Wiem, że Clojure może przekształcić dowolną ze swoich trwałych kolekcji w kolekcję „przejściową” (tj. Zmienną z liniowo typowanymi funkcjami mutacji) i O(1)stopniowo z powrotem w trwałą . To wygląda się O(n), mimo że zależy oczywiście od tego jak sprytny realizacji ++JEST.
Jörg W Mittag,
1
@ Jörg - Jestem prawie pewien, że ten jest O(n). W granicach możliwości zmiany wszystkiego musi być O(n), chociaż możesz spróbować odłożyć tworzenie nowej kopii, aby zaoszczędzić czas, lub podwoić czas dostępu, czytając zestawy zmian zamiast oryginalnej mapy. To, który z nich działa najlepiej, zależy prawdopodobnie od twojego przypadku użycia.
Rex Kerr,
1
@Rustem - Mapy są nieuporządkowane. Pojawią się w dowolnej kolejności (w przypadku mapy skrótów jest to zazwyczaj kolejność klucza). W szczególności niezmienne mapy mają specjalne przypadki dla naprawdę małych map, które różnią się od map zmiennych.
Rex Kerr,
@Rustem Mapy nie są zamówione.
Daniel C. Sobral,
5

Począwszy od Scala 2.13konstruktorów fabryk zastosowanych w .to(factory):

Map(1 -> "a", 2 -> "b").to(collection.mutable.Map)
// collection.mutable.Map[Int,String] = HashMap(1 -> "a", 2 -> "b")
Xavier Guihot
źródło
4

A co powiesz na używanie 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
źródło
To jest fajne, ale w zasadzie robi to samo, co mutable.Map#applyz nieco bardziej szablonowe.
Kevin Wright,
2

Istnieje wariant tworzenia pustej mutacji, Mapktóra ma wartości domyślne pobrane z niezmiennej Map. W dowolnym momencie możesz zapisać wartość i zastąpić wartość domyślną:

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

Uwaga (patrz komentarz Rexa Kerra): Nie będziesz w stanie usunąć elementów pochodzących z niezmiennej mapy:

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

scala> mMap(1)
//res6: String = one
Aleksandra Azarowa
źródło
3
Jest to przydatne w niektórych przypadkach, ale pamiętaj, że nie możesz usunąć elementu z nowej mapy, który był obecny na twojej domyślnej mapie; możesz tylko zakrywać i odkrywać wartości domyślne.
Rex Kerr,
Racja, to rozwiązanie jest częściowe.
Alexander Azarov,
0

W scali 2.13 istnieją dwie alternatywy: tometoda instancji mapy źródłowej lub frommetoda obiektu towarzyszącego mapy docelowej.

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)

Jak widać, o mutable.Mapwdrożeniu zadecydowała biblioteka. Jeśli chcesz wybrać konkretną implementację, na przykład mutable.HashMap, zastąpić wszystkie wystąpienia mutable.Mapz mutable.HashMap.

Readren
źródło