具有可变默认值的 Scala Map 始终指向同一个对象

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

Scala version:3.1

我想创建一个 String -> Int 映射,每个键可以指向多个 Int。

因此我选择默认可变的地图Buffer[Int]

但是似乎所有的键总是指向同一个Buffer[Int]

请注意,第一个 m("a") += 1 也很奇怪,在此 += 之后它什么也不打印,但默认值已更改,因此它打印 two 1 在下 println.

此外,如何解决 mutable.Map+=Buffer+= 覆盖的问题。如果密钥不存在,我希望地图插入一个新的 Buffer,然后调用 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

TL;DR: withDefaultValue 在这里没用。

注意 signature of withDefaultValue 是:

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

参数d取值,而不是by name=> V),因此只计算一次。因此,原始映射中不存在的任何键都将 return 与您创建一次的空缓冲区相同。

您得到的 withDefaultValue(和 withDefault)是一个新的 map-like 对象,而不是原始地图。只有分配结果 (m("a") = ...) 才会改变它。

Note: The default is only used for apply. Other methods like get, contains, iterator, keys, etc. are not affected by withDefaultValue.

考虑这个例子:

val m = new HashMap[Integer, Buffer[String]]()
val mm = m.withDefault(_ => Buffer.empty)
mm(1).append("4")
mm(1) // ArrayBuffer()

你可能想要的是getOrElseUpdate(如果你愿意,你可以定义一个辅助函数)带有签名

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

注意这次如何按名称调用 defaultValue,每次调用都会创建一个新缓冲区:

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