Bagaimana cara mengubah immutable.Map menjadi mutable.Map di Scala?

93

Bagaimana cara mengonversi immutable.Mapke mutable.Mapdalam Scala sehingga saya dapat memperbarui nilai di Map?

Łukasz Lew
sumber

Jawaban:

126

Cara terbersih adalah dengan menggunakan mutable.Mappabrik varargs. Berbeda dengan ++pendekatan, ini menggunakan CanBuildFrommekanisme, dan berpotensi menjadi lebih efisien jika kode perpustakaan ditulis untuk memanfaatkan ini:

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

Ini berfungsi karena a Mapjuga dapat dilihat sebagai urutan Pasangan.

Kevin Wright
sumber
2
Bisakah Anda menjelaskan, sintaks apa yang Anda gunakan di baris kedua saat meneruskan parameter? Apa fungsi usus besar?
Heinzi
7
: _*sangat mirip dengan tipe ascription, yang memberi tahu kompiler tipe apa yang harus ditambahkan ke ekspresi yang diberikan. Anda dapat menganggapnya di sini seperti mengatakan "ambillah urutan ini, dan perlakukan itu sebagai sejumlah parameter vararg".
Kevin Wright
16
Ada yang salah dengan perpustakaan koleksi jika ini yang terbersih;)
matanster
2
@matt Itu bisa dibuat sedikit lebih pendek dengan impor alias, tetapi perlu diingat bahwa mengorbankan kekekalan sangat non-idiomatis untuk Scala, bukan hal yang saya dorong dengan membuatnya terlihat lebih mudah ... Karena penasaran , bagaimana lagi Anda bisa mengusulkan melakukannya dengan lebih rapi, jika tidak melalui salinan?
Kevin Wright
Itu maksud saya, saya tidak bisa, tetapi perpustakaan koleksi yang lebih baik bisa membuat ini mungkin, IMHO.
matanster
41
val myImmutableMap = collection.immutable.Map(1->"one",2->"two")
val myMutableMap = collection.mutable.Map() ++ myImmutableMap
Rex Kerr
sumber
1
Tahukah Anda betapa rumitnya waktu asimtotik dari hal ini? Saya tahu bahwa Clojure dapat mengubah koleksi persistennya menjadi koleksi "sementara" (yaitu koleksi yang dapat berubah dengan fungsi mutasi tipe linier) dan kembali menjadi koleksi persisten dalam beberapa O(1)langkah. Ini terlihat menjadi O(n), walaupun itu tentu saja tergantung pada seberapa pintar pelaksanaan ++yaitu.
Jörg W Mittag
1
@ Jörg - Saya cukup yakin yang satu ini O(n). Dalam batas saat Anda mengubah segalanya, itu harus O(n), meskipun Anda dapat mencoba untuk menunda pembuatan salinan baru untuk menghemat waktu, atau Anda menggandakan waktu akses Anda dengan membaca set perubahan daripada peta asli. Mana yang berkinerja terbaik mungkin tergantung pada kasus penggunaan Anda.
Rex Kerr
1
@Rustem - Peta tidak diurutkan. Mereka akan muncul dalam urutan yang mereka inginkan (dengan peta hash, ini biasanya urutan kunci hash). Secara khusus, peta yang tidak dapat diubah memiliki kasus khusus untuk peta yang sangat kecil yang berbeda dari peta yang dapat berubah.
Rex Kerr
@Rustem Maps tidak dipesan.
Daniel C. Sobral
4

Bagaimana kalau menggunakan 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
sumber
Ini adalah keren, tapi pada dasarnya melakukan hal yang sama seperti mutable.Map#applydengan sedikit lebih boilerplate.
Kevin Wright
4

Memulai Scala 2.13, melalui pembangun pabrik yang diterapkan dengan .to(factory):

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

Ada varian untuk membuat bisa berubah kosong Mapyang memiliki nilai default yang diambil dari yang tidak bisa diubah Map. Anda dapat menyimpan nilai dan mengganti default kapan saja:

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

Peringatan (lihat komentar oleh Rex Kerr): Anda tidak akan dapat menghapus elemen yang berasal dari peta yang tidak dapat diubah:

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

scala> mMap(1)
//res6: String = one
Alexander Azarov
sumber
3
Ini berguna dalam beberapa kasus, tetapi perhatikan bahwa Anda tidak dapat menghapus elemen di peta baru Anda yang ada di peta default Anda; Anda hanya dapat menutupi dan mengungkap default.
Rex Kerr
Benar, solusi ini parsial.
Alexander Azarov