Как преобразовать immutable.Map в mutable.Map в Scala?

95

Каким образом можно преобразовать immutable.Mapв mutable.Mapв Scala , так что я могу обновить значения в Map?

Лукаш Лью
источник

Ответы:

131

Самый чистый способ - использовать mutable.Mapфабрику varargs. В отличие от ++подхода, здесь используется CanBuildFromмеханизм, и поэтому он может быть более эффективным, если библиотечный код был написан с учетом этого:

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

Это работает, потому что a Mapтакже можно рассматривать как последовательность пар.

Кевин Райт
источник
2
Вы можете объяснить, какой синтаксис вы используете во второй строке при передаче параметра? Что делает толстая кишка?
Heinzi 01
7
: _*очень похоже на присвоение типа, сообщая компилятору, какой именно тип присвоить данному выражению. Вы можете думать об этом здесь как о том, чтобы «взять эту последовательность и рассматривать ее как несколько параметров vararg».
Кевин Райт
16
Что-то не так с библиотеками коллекций, если она самая чистая;)
matanster
2
@matt Его можно было бы сделать немного короче с помощью импорта с псевдонимом, но имейте в виду, что принесение в жертву неизменяемости для Scala очень неидиоматично, не совсем то, чего я бы одобрил, сделав его еще проще ... Из любопытства , как еще можно предложить сделать это более чисто, как не через копию?
Кевин Райт
Это моя точка зрения, я не могу, но лучшая библиотека коллекций могла бы сделать это возможным, ИМХО.
Matanster 08
42
val myImmutableMap = collection.immutable.Map(1->"one",2->"two")
val myMutableMap = collection.mutable.Map() ++ myImmutableMap
Рекс Керр
источник
1
Знаете ли вы, что это асимптотическая временная сложность? Я знаю, что Clojure может поэтапно превратить любую из своих постоянных коллекций в «временную» (т.е. изменяемую с линейно типизированными функциями мутации) и обратно в постоянную O(1). Это выглядит быть O(n), хотя это зависит, конечно , от того, насколько умный реализации ++ИС.
Jörg W Mittag
1
@ Йорг, я почти уверен, что это так O(n). В пределе, когда вы меняете все, это должно быть O(n), хотя вы можете попытаться отложить создание новой копии, чтобы сэкономить время, или вы удвоите время доступа, читая наборы изменений вместо исходной карты. Какой из них работает лучше всего, вероятно, зависит от вашего варианта использования.
Рекс Керр,
1
@Rustem - Карты неупорядочены. Они будут появляться в любом порядке (в случае хеш-карты это обычно порядок хеш-ключа). В частности, неизменяемые карты имеют особые случаи для действительно крошечных карт, которые отличаются от изменяемых карт.
Рекс Керр
@Rustem Карты не заказываются.
Дэниел С. Собрал
5

Запускается Scala 2.13заводскими сборщиками с применением .to(factory):

Map(1 -> "a", 2 -> "b").to(collection.mutable.Map)
// collection.mutable.Map[Int,String] = HashMap(1 -> "a", 2 -> "b")
Ксавье Гихо
источник
4

Как насчет использования 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
источник
Это является прохладно, но в основном делает то же самое , что и mutable.Map#applyс немного больше шаблоннога.
Кевин Райт
2

Существует вариант создания пустого изменяемого объекта Mapсо значениями по умолчанию, взятыми из неизменяемого Map. Вы можете сохранить значение и изменить значение по умолчанию в любое время:

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

Предостережение (см. Комментарий Рекса Керра): вы не сможете удалить элементы, поступающие с неизменяемой карты:

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

scala> mMap(1)
//res6: String = one
Александр Азаров
источник
3
Это полезно в некоторых случаях, но учтите, что вы не можете удалить элемент в вашей новой карте, который присутствовал в вашей карте по умолчанию; вы можете только покрыть и раскрыть значения по умолчанию.
Рекс Керр
Да, это частичное решение.
Александр Азаров
0

В scala 2.13 есть две альтернативы: toметод экземпляра исходной карты или fromметод сопутствующего объекта целевой карты.

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)

Как видите, mutable.Mapреализация решила библиотека. Если вы хотите выбрать конкретную реализацию, например mutable.HashMap, заменить все вхождения mutable.Mapс mutable.HashMap.

Readren
источник