Scala 中的嵌套默认映射
我正在尝试在 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
m
res : scala.collection.mutable.Map[Int,scala.collection.mutable.Map[Int,Int]] = Map()
因此,当通过适当的键寻址时,地图会返回我输入的内容。但是,地图本身显示为空!在此示例中,甚至 m.size 也返回 0。谁能解释一下这是怎么回事?
I'm trying to construct nested maps in Scala, where both the outer and inner map use the "withDefaultValue" method. For example, the following :
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()
So the map, when addressed by the appropriate keys, gives me back what I put in. However, the map itself appears empty! Even m.size returns 0 in this example. Can anyone explain what's going on here?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
简短的回答
这绝对不是一个错误。
长答案
withDefaultValue 的行为是在 Map 中存储默认值(在您的情况下是可变映射),以便在键不存在时返回。这与未找到键时插入到 Map 中的值不同。
让我们仔细看看发生了什么。如果我们将默认映射作为单独的变量取出,以便我们可以随意检查,会更容易理解;我们称其为
default
所以
default
是一个可变映射(有自己的默认值)。现在我们可以创建m
并指定default
作为默认值。现在,每当使用丢失的密钥访问
m
时,它将返回default
。请注意,这与您的行为完全相同,因为withDefaultValue
定义为:注意,它是
d: B
而不是d: =>; B
,所以每次默认访问时不会创建新的地图;它将返回完全相同的对象,我们称之为default
。让我们看看会发生什么:
由于键 1 不在
m
中,因此返回默认值default
。此时的default
是一个空的Map。由于
m(1)
返回default
,因此此操作将 5 作为键 2 的值存储在default
中。没有向 Mapm
写入任何内容,因为m(1)
解析为default
,它完全是一个单独的 Map。我们可以通过查看default
来检查这一点:但正如我们所说,
m
保持不变现在,如何实现您真正想要的?您不想使用
withDefaultValue
,而是希望使用getOrElseUpdate
:注意我们如何看到
op: => B?这意味着每次需要时都会重新评估参数
op
。这允许我们在其中放置一个新的 Map,并使其成为每个无效键的单独的新 Map。让我们看一下:这里不需要默认值。
键 1 不存在,因此我们插入一个新的 HashMap,并返回该新值。我们可以检查它是否按照我们的预期插入。请注意,1 映射到新添加的空映射,并且由于上面解释的行为,它们 3 没有添加到任何地方。
同样,我们可以按预期更新地图:
并检查它是否已添加:
Short answer
It's definitely not a bug.
Long answer
The behavior of
withDefaultValue
is to store a default value (in your case, a mutable map) inside the Map to be returned in the case that they key does not exist. This is not the same as a value that is inserted into the Map when they key is not found.Let's look closely at what's happening. It will be easier to understand if we pull the default map out as a separate variable so we can inspect is at will; let's call it
default
So
default
is a mutable map (that has its own default value). Now we can createm
and givedefault
as the default value.Now whenever
m
is accessed with a missing key, it will returndefault
. Notice that this is the exact same behavior as you have becausewithDefaultValue
is defined as:Notice that it's
d: B
and notd: => B
, so it will not create a new map each time the default is accessed; it will return the same exact object, what we've calleddefault
.So let's see what happens:
Since key 1 is not in
m
, the default,default
is returned.default
at this time is an empty Map.Since
m(1)
returnsdefault
, this operation stores 5 as the value for key 2 indefault
. Nothing is written to the Mapm
becausem(1)
resolves todefault
which is a separate Map entirely. We can check this by viewingdefault
:But as we said,
m
is left unchangedNow, how to achieve what you really wanted? Instead of using
withDefaultValue
, you want to make use ofgetOrElseUpdate
:Notice how we see
op: => B
? This means that the argumentop
will be re-evaluated each time it is needed. This allows us to put a new Map in there and have it be a separate new Map for each invalid key. Let's take a look:No default values needed here.
Key 1 doesn't exist, so we insert a new HashMap, and return that new value. We can check that it was inserted as we expected. Notice that 1 maps to the newly added empty map and that they 3 was not added anywhere because of the behavior explained above.
Likewise, we can update the Map as expected:
and check that it was added:
withDefaultValue
用于在未找到键时返回值。它不会填充地图。所以你的地图保持空白。有点像使用getOrElse(a, b)
,其中b
由withDefaultValue
提供。withDefaultValue
is used to return a value when the key was not found. It does not populate the map. So you map stays empty. Somewhat like usinggetOrElse(a, b)
whereb
is provided bywithDefaultValue
.我刚刚遇到了完全相同的问题,很高兴找到 dhg 的答案。由于一直输入 getOrElseUpdate 不太简洁,所以我想出了这个我想分享的想法的小扩展:
您可以声明一个使用 getOrElseUpdate 作为 () 运算符的默认行为的类:
现在您可以像这样执行您想要执行的操作:
现在,这会导致
map
包含 5 (或者更确切地说:包含DefaultDict 包含键 2) 的值 5。I just had the exact same problem, and was happy to find dhg's answer. Since typing getOrElseUpdate all the time is not very concise, I came up with this little extension of the idea that I want to share:
You can declare a class that uses getOrElseUpdate as default behavior for the () operator:
Now you can do what you want to do like this:
Which does now result in
map
containing 5 (or rather: containing a DefaultDict containing the value 5 for the key 2).您所看到的是您创建了一个
Map[Int, Int]
的效果,只要键不在外部映射中,这就是默认值。为了获得您正在寻找的效果,您必须使用一个实现来包装
Map
,当在Map
中找不到某个键时,该实现实际上会插入默认值代码>.编辑:
我不确定您的实际用例是什么,但使用一对作为单个
Map.
What you're seeing is the effect that you've created a single
Map[Int, Int]
this is the default value whenever the key isn't in the outer map.To get the effect that you're looking for, you'll have to wrap the
Map
with an implementation that actually inserts the default value when a key isn't found in theMap
.Edit:
I'm not sure what your actual use case is, but you may have an easier time using a pair for the key to a single
Map
.我知道有点晚了,但我刚刚在尝试解决同样的问题时看到了该帖子。
该 API 可能与 2012 版本不同,但您可能希望使用
withDefault
而不是withDefaultValue
。不同之处在于
withDefault
采用一个函数作为参数,每次请求丢失的按键时都会执行该函数;)I know it's a bit late but I've just seen the post while I was trying to solve the same problem.
Probably the API are different from the 2012 version but you may want to use
withDefault
instead thatwithDefaultValue
.The difference is that
withDefault
takes a function as parameter, that is executed every time a missed key is requested ;)