Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Scala Map with mutable default value always point to the same object

Scala version:3.1

I want to create a String -> Int map, that each key may point to many Int.

Therefore I choose map with default mutable Buffer[Int].

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

But it seems the all the keys always point to the same Buffer[Int]

Notice it is also weird in the first m("a") += 1 that, it print out nothing after this += but default value changed so that it print two 1 in the next println.

Also, how I can fix the problem that mutable.Map‘s += is overriden by Buffer‘s +=. I am expecting the map insert a new Buffer if the key doens’t exist, then call += of the buffer.

  import scala.collection.mutable
  val m: mutable.Map[String, mutable.Buffer[Int]] =
    mutable.HashMap.empty.withDefaultValue(mutable.Buffer.empty)

  m("a") += 1
  println(m) // Map()

  m("a") = m("a") += 1

  println(m) // Map(a -> ArrayBuffer(1, 1))

  m("b") = m("b") += 2 // Map(a -> ArrayBuffer(1, 1, 2), b -> ArrayBuffer(1, 1, 2)) , not expecting this, key is correct but

  println(m) // Map(a -> ArrayBuffer(1, 1, 2), b -> ArrayBuffer(1, 1, 2))

  m("a") = m("a") += 2

  println(m) // Map(a -> ArrayBuffer(1, 1, 2, 2), b -> ArrayBuffer(1, 1, 2, 2)) , 
  // this is not as I expected. The keys are correct, however their values are all the same ArrayBuffer

>Solution :

TL;DR: withDefaultValue is useless here.

Note the signature of withDefaultValue is:

def withDefaultValue(d: V): Map[K, V]

The parameter d is taken by value, not by name (=> V), therefore it is only evaluated once. Therefore any key not present in the original map will return the same empty buffer you created once.

What you probably want is getOrElseUpdate (you can define a helper function if you’d like) with the signature

getOrElseUpdate(key: K, defaultValue: => V): V

Note how defaultValue is called by name this time, and each call will create a new buffer:

val m: mutable.Map[String, mutable.Buffer[Int]] =
    mutable.HashMap.empty

def mm(key: String): mutable.Buffer[Int] = m.getOrElseUpdate(key, mutable.Buffer.empty)

mm("a") += 1
mm("b") += 2
// now buffers at `a` and `b` are distinct
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading