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

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

提问人:worldterminator 提问时间:3/7/2022 更新时间:3/7/2022 访问量:187

问:

Scala 版本:3.1

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

因此,我选择默认可变的映射。Buffer[Int]

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

请注意,在第一个中也很奇怪,在此之后它不打印任何内容,但默认值发生了变化,因此它在下一个中打印了两个 1 m("a") += 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
斯卡拉 数据结构 可变

评论

0赞 Luis Miguel Mejía Suárez 3/7/2022
你真的需要这么多的可变性吗?

答:

4赞 Koterpillar 3/7/2022 #1

TL;DR:在这里没用。withDefaultValue

请注意,withDefaultValue 的签名为:

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

该参数是按值而不是按名称 () 获取的,因此仅计算一次。因此,原始映射中不存在的任何键都将返回您创建的相同空缓冲区。d=> V

你得到的(和)是一个新的类似地图的对象,而不是原始地图。只有赋值结果 () 才会改变它。withDefaultValuewithDefaultm("a") = ...

注意:默认值仅用于应用。其他方法(如 get、contains、iterator、keys 等)不受 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