简答
这绝对不是错误。
长答案
withDefaultValue 的行为是将默认值(在您的情况下为可变映射)存储在要返回的 Map 中,以防它们的键不存在。这与未找到键时插入到 Map 中的值不同。
让我们仔细看看发生了什么。如果我们将默认映射作为一个单独的变量拉出来,这样我们就可以随意检查,这将更容易理解;我们就叫它default
import collection.mutable.HashMap
val default = HashMap.empty[Int,Int].withDefaultValue(3)
所以default 是一个可变映射(有自己的默认值)。现在我们可以创建m 并将default 作为默认值。
import collection.mutable.{Map => MMap}
val m = HashMap.empty[Int, MMap[Int,Int]].withDefaultValue(default)
现在,每当使用缺少的键访问 m 时,它将返回 default。请注意,这与您的行为完全相同,因为 withDefaultValue 定义为:
def withDefaultValue (d: B): Map[A, B]
注意是d: B而不是d: => B,所以每次访问默认不会创建新地图;它将返回完全相同的对象,我们称之为default。
那么让我们看看会发生什么:
m(1) // Map()
由于密钥 1 不在 m 中,因此返回默认值 default。 default此时是一个空地图。
m(1)(2) = 5
由于m(1) 返回default,此操作将5 作为键2 的值存储在default 中。没有任何内容写入地图m,因为m(1) 解析为default,这完全是一个单独的地图。我们可以通过查看default来检查这一点:
default // Map(2 -> 5)
但正如我们所说,m 保持不变
m // Map()
现在,如何实现你真正想要的?而不是使用withDefaultValue,你想使用getOrElseUpdate:
def getOrElseUpdate (key: A, op: ⇒ B): B
注意我们如何看到op: => B?这意味着每次需要时都会重新评估参数op。这允许我们在其中放置一个新 Map 并让它成为每个无效键的单独新 Map。一起来看看吧:
val m2 = HashMap.empty[Int, MMap[Int,Int]]
这里不需要默认值。
m2.getOrElseUpdate(1, HashMap.empty[Int,Int].withDefaultValue(3)) // Map()
键 1 不存在,因此我们插入一个新的 HashMap,并返回该新值。我们可以检查它是否按预期插入。请注意,1 映射到新添加的空映射,并且由于上述行为,它们 3 没有添加到任何地方。
m2 // Map(1 -> Map())
同样,我们可以按预期更新地图:
m2.getOrElseUpdate(1, HashMap.empty[Int,Int].withDefaultValue(1))(2) = 6
并检查它是否已添加:
m2 // Map(1 -> Map(2 -> 6))