Як я можу перетворити незмінний.Map на змінний.Map у Scala?


Відповіді:


126

Найчистішим способом було б використання mutable.Mapфабрики вараргів. На відміну від ++підходу, тут використовується CanBuildFromмеханізм, і тому потенціал може бути більш ефективним, якщо бібліотечний код був написаний, щоб скористатися цим:

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

Це працює, оскільки a Mapтакож можна розглядати як послідовність пар.


2
Чи можете ви пояснити, який синтаксис ви використовуєте у другому рядку при передачі параметра? Що робить товста кишка?
Heinzi

7
: _*нагадує атрибуцію типу, повідомляючи компілятору, який саме тип призначати даному виразу. Тут ви можете думати про це як про те, що "прийміть цю послідовність і поводьтеся з нею як безліч параметрів vararg".
Кевін Райт,

16
Щось не так з бібліотеками колекцій, якщо це найчистіше;)
matanster

2
Це може @ Matt бути трохи коротше з псевдонімами імпорту, але майте на увазі , що жертвуючи незмінність є дуже НЕ-ідіоматичних для Scala, точно не та річ , я б Enourage, зробивши його виглядати навіть простіше ... з цікавості , як інакше ви могли б запропонувати зробити це більш чисто, як не за допомогою копії?
Kevin Wright

Це моя думка, я не можу, але краща бібліотека колекцій могла б зробити це можливо, ІМХО.
matanster

41
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 не замовляються.
Daniel C. Sobral

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)

Це є прохолодно, але в основному робить те ж саме , що і mutable.Map#applyз трохи більше шаблоннога.
Kevin Wright

4

Починаючи Scala 2.13від будівельників заводів, які застосовуються з .to(factory):

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

1

Існує варіант створення порожньої змінної, 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
Це корисно в деяких випадках, але зауважте, що ви не можете видалити елемент на новій карті, який був на вашій карті за замовчуванням; Ви можете лише покрити та розкрити за замовчуванням.
Рекс Керр,

Правильно, це рішення часткове.
Олександр Азаров
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.