Como posso converter immutable.Map em mutable.Map no Scala?

93

Como posso converter immutable.Mappara mutable.Mapno Scala para que eu possa atualizar os valores em Map?

Łukasz Lew
fonte

Respostas:

126

A maneira mais limpa seria usar a mutable.Mapfábrica de Varargs. Ao contrário da ++abordagem, ele usa o CanBuildFrommecanismo e, portanto, tem o potencial de ser mais eficiente se o código da biblioteca foi escrito para tirar proveito disso:

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

Isso funciona porque a Maptambém pode ser visto como uma sequência de pares.

Kevin Wright
fonte
2
Você pode explicar, qual sintaxe você usa na segunda linha ao passar o parâmetro? O que o cólon faz?
Heinzi 01 de
7
: _*é muito parecido com a atribuição de tipo, dizendo ao compilador exatamente que tipo atribuir a uma determinada expressão. Você pode pensar nisso aqui como dizer "pegue esta sequência e trate-a como uma série de parâmetros vararg."
Kevin Wright
16
Há algo errado com as bibliotecas de coleção se esta for a mais limpa;)
matanster
2
@matt Poderia ser um pouco mais curto com importações com alias, mas tenha em mente que sacrificar a imutabilidade é muito não idiomático para Scala, não exatamente o tipo de coisa que eu encorajaria fazendo parecer ainda mais fácil ... Por curiosidade , de que outra forma você poderia propor fazê-lo de forma mais limpa, se não por meio de uma cópia?
Kevin Wright
Esse é o meu ponto, eu não posso, mas uma biblioteca de coleções melhor poderia tornar isso possível, IMHO.
matanster
41
val myImmutableMap = collection.immutable.Map(1->"one",2->"two")
val myMutableMap = collection.mutable.Map() ++ myImmutableMap
Rex Kerr
fonte
1
Você sabe qual é a complexidade de tempo assintótica disso? Eu sei que o Clojure pode transformar qualquer uma de suas coleções persistentes em uma "transitória" (ou seja, uma mutável com funções de mutação de tipo linear) e de volta em uma persistente em O(1)etapas. Este parece ser O(n), embora isso depende, naturalmente, como inteligente a implementação de ++SI.
Jörg W Mittag
1
@ Jörg - Tenho certeza que esse é O(n). No limite, conforme você muda tudo, deve ser O(n), embora você possa tentar adiar a criação da nova cópia para economizar tempo, ou dobrar seu tempo de acesso lendo changesets em vez do mapa original. O que tem melhor desempenho provavelmente depende do seu caso de uso.
Rex Kerr
1
@Rustem - Mapas não ordenados. Eles aparecerão na ordem que quiserem (com um mapa hash, normalmente é a ordem da chave hash). Em particular, os mapas imutáveis ​​têm casos especiais para mapas realmente pequenos que são diferentes dos mapas mutáveis.
Rex Kerr
Mapas @Rustem não são pedidos.
Daniel C. Sobral
4

Que tal usar 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
fonte
Ele é legal, mas faz basicamente a mesma coisa que mutable.Map#applycom um pouco mais clichê.
Kevin Wright de
2

Começando Scala 2.13, por meio de construtores de fábrica aplicados com .to(factory):

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

Existe uma variante para criar um mutável vazio Mapque tem valores padrão retirados do imutável Map. Você pode armazenar um valor e substituir o padrão a qualquer 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

Advertência (ver o comentário de Rex Kerr): Você não será capaz de remover os elementos vindos do mapa imutável:

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

scala> mMap(1)
//res6: String = one
Alexander Azarov
fonte
3
Isso é útil em alguns casos, mas observe que você não consegue remover um elemento em seu novo mapa que estava presente em seu mapa padrão; você só pode cobrir e descobrir os padrões.
Rex Kerr
Certo, essa solução é parcial.
Alexander Azarov