Scala中的嵌套默认映射
我试图在Scala中构造嵌套映射,其中外部映射和内部映射都使用“withDefaultValue”方法。例如,以下各项:Scala中的嵌套默认映射,scala,map,default,Scala,Map,Default,我试图在Scala中构造嵌套映射,其中外部映射和内部映射都使用“withDefaultValue”方法。例如,以下各项: val m = HashMap.empty[Int, collection.mutable.Map[Int,Int]].withDefaultValue( HashMap.empty[Int,Int].withDefaultValue(3)) m(1)(2) res: Int = 3 m(1)(2) = 5 m(1)(2) res: Int = 5 m(2)(3) = 6
val m = HashMap.empty[Int, collection.mutable.Map[Int,Int]].withDefaultValue( HashMap.empty[Int,Int].withDefaultValue(3))
m(1)(2)
res: Int = 3
m(1)(2) = 5
m(1)(2)
res: Int = 5
m(2)(3) = 6
m
res : scala.collection.mutable.Map[Int,scala.collection.mutable.Map[Int,Int]] = Map()
因此,当用适当的键对地图进行寻址时,它会将我输入的内容返回给我。但是,地图本身显示为空!在本例中,即使m.size也返回0。有人能解释一下这里发生了什么吗?
withDefaultValue
用于在找不到键时返回一个值。它不会填充地图。所以你的地图是空的。有点像使用getOrElse(a,b)
其中b
由withDefaultValue
提供,您看到的效果是创建了一个映射[Int,Int]
只要键不在外部映射中,这就是默认值
scala> val m = HashMap.empty[Int, collection.mutable.Map[Int,Int]].withDefaultValue( HashMap.empty[Int,Int].withDefaultValue(3))
m: scala.collection.mutable.Map[Int,scala.collection.mutable.Map[Int,Int]] = Map()
scala> m(2)(2)
res1: Int = 3
scala> m(1)(2) = 5
scala> m(2)(2)
res2: Int = 5
要获得您想要的效果,您必须使用一个实现来包装映射
,该实现在映射
中找不到键时实际插入默认值
scala> val m = HashMap.empty[(Int, Int), Int].withDefaultValue(3)
m: scala.collection.mutable.Map[(Int, Int),Int] = Map()
scala> m((1, 2))
res0: Int = 3
scala> m((1, 2)) = 5
scala> m((1, 2))
res3: Int = 5
scala> m
res4: scala.collection.mutable.Map[(Int, Int),Int] = Map((1,2) -> 5)
编辑:
我不确定您的实际用例是什么,但是您可以更轻松地使用一对作为单个映射的键
scala> val m = HashMap.empty[(Int, Int), Int].withDefaultValue(3)
m: scala.collection.mutable.Map[(Int, Int),Int] = Map()
scala> m((1, 2))
res0: Int = 3
scala> m((1, 2)) = 5
scala> m((1, 2))
res3: Int = 5
scala> m
res4: scala.collection.mutable.Map[(Int, Int),Int] = Map((1,2) -> 5)
简短回答
这绝对不是一个bug
长话短说
withDefaultValue
的行为是在映射内存储一个默认值(在您的情况下,是一个可变映射),如果它们的键不存在,则返回该映射。这与未找到它们的键时插入到映射中的值不同
让我们仔细看看发生了什么。如果我们把默认映射作为一个单独的变量拉出来,这样就更容易理解了,这样我们就可以随意检查is;让我们称之为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
<代码>默认值
此时为空映射
m(1)(2) = 5
由于m(1)
返回default
,此操作将5存储为default
中键2的值。没有任何内容写入映射m
,因为m(1)
解析为default
,这是一个完全独立的映射。我们可以通过查看默认值来检查这一点:
default // Map(2 -> 5)
但正如我们所说,m
保持不变
m // Map()
现在,如何实现你真正想要的?您希望使用getOrElseUpdate
,而不是使用withDefaultValue
:
def getOrElseUpdate (key: A, op: ⇒ B): B
注意我们是如何看到的op:=>B
?这意味着参数op
将在每次需要时重新计算。这允许我们在其中放置一个新映射,并为每个无效密钥创建一个单独的新映射。让我们来看一看:
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))
我只是遇到了完全相同的问题,很高兴找到了dhg的答案。由于一直键入GetOrelsUpdate并不十分简洁,因此我想与大家分享一下我的想法:
您可以声明一个类,该类使用GetOrelsUpdate作为()运算符的默认行为:
class DefaultDict[K, V](defaultFunction: (K) => V) extends HashMap[K, V] {
override def default(key: K): V = return defaultFunction(key)
override def apply(key: K): V =
getOrElseUpdate(key, default(key))
}
现在,您可以这样做:
var map = new DefaultDict[Int, DefaultDict[Int, Int]](
key => new DefaultDict(key => 3))
map(1)(2) = 5
现在,这确实导致map
包含5(或者更确切地说:包含一个DefaultDict,其中包含键2的值5)。我知道有点晚了,但我刚刚在尝试解决相同问题时看到了这篇文章。
该API可能与2012版本不同,但您可能希望使用with default
而不是with defaultvalue
不同之处在于,withDefault
将函数作为参数,每次请求缺少的键时都会执行该函数;) 当然可以,但是当我调用m(1)(2)=5时,应该在映射中放置值5。那么,为什么在执行这个“update()”之后,地图看起来是空的呢?@nomad我对这里有趣的行为很感兴趣,但实际上这一切都很有意义。更新后m仍然为空,因为您更新的不是m;您更新了m的一个默认值。也就是说,您更新了由m(1)引用的映射,直到m(1)获得实际值。因为你改变了地图的大小,而且你从来没有更新过m的键值对,所以m仍然是空的;崩溃帮了大忙。有趣的是,我最初对函数应该如何工作的偏见是如何强烈地阻止我看到实际发生的事情。请不要。谢谢