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]
.
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
CodePudding user response:
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.
And what you get withDefaultValue
(and withDefault
) is a new map-like object, not the original map. Only assigning the results (m("a") = ...
) will change it.
Note: The default is only used for apply. Other methods like get, contains, iterator, keys, etc. are not affected by withDefaultValue.
Consider this example:
val m = new HashMap[Integer, Buffer[String]]()
val mm = m.withDefault(_ => Buffer.empty)
mm(1).append("4")
mm(1) // ArrayBuffer()
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
CodePudding user response:
You can use withDefault, as is passes the parameter as a function, not by value as withDefaultValue
does, therefore it is evaluated again each time:
val m: mutable.Map[String, mutable.Buffer[Int]] =
mutable.HashMap.empty.withDefault(_ => mutable.Buffer.empty)