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


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.


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

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)

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")

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

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
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.