scala 如何访问/初始化和更新可变映射中的值?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15505048/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 05:04:22  来源:igfitidea点击:

How to access/initialize and update values in a mutable map?

scaladictionary

提问by bluenote10

Consider the simple problem of using a mutable map to keep track of occurrences/counts, i.e. with:

考虑使用可变映射来跟踪出现/计数的简单问题,即:

val counts = collection.mutable.Map[SomeKeyType, Int]()

My current approach to incrementing a count is:

我目前增加计数的方法是:

counts(key) = counts.getOrElse(key, 0) + 1
// or equivalently
counts.update(key, counts.getOrElse(key, 0) + 1)

This somehow feels a bit clumsy, because I have to specify the key twice. In terms of performance, I would also expect that keyhas to be located twice in the map, which I would like to avoid. Interestingly, this access and update problem would not occur if Intwould provide some mechanism to modify itself. Changing from Intto a Counterclass that provides an incrementfunction would for instance allow:

这在某种程度上感觉有点笨拙,因为我必须两次指定密钥。在性能方面,我还希望它key必须在地图中出现两次,我想避免这种情况。有趣的是,如果Int提供某种自我修改机制,就不会发生这种访问和更新问题。例如,将 from 更改IntCounter提供increment函数的类将允许:

// not possible with Int
counts.getOrElseUpdate(key, 0) += 1
// but with a modifiable counter
counts.getOrElseUpdate(key, new Counter).increment

Somehow I'm always expecting to have the following functionality with a mutable map (somewhat similar to transformbut without returning a new collection and on a specific key with a default value):

不知何故,我总是期望具有可变映射的以下功能(有点类似于transform但不返回新集合和具有默认值的特定键):

// fictitious use
counts.updateOrElse(key, 0, _ + 1)
// or alternatively
counts.getOrElseUpdate(key, 0).modify(_ + 1)

However as far as I can see, such a functionality does not exist. Wouldn't it make sense in general (performance and syntax wise) to have such a f: A => Ain-place modification possibility? Probably I'm just missing something here... I guess there must be some better solution to this problem making such a functionality unnecessary?

但是,据我所知,这样的功能并不存在。拥有这种f: A => A就地修改的可能性在一般情况下(性能和语法明智)难道没有意义吗?可能我只是在这里遗漏了一些东西......我想必须有更好的解决方案来解决这个问题,从而使这样的功能变得不必要?

Update:

更新:

I should have clarified that I'm aware of withDefaultValuebut the problem remains the same: performing two lookups is still twiceas slow than one, no matter if it is a O(1) operation or not. Frankly, in many situations I would be more than happy to achieve a speed-up of factor 2. And obviously the construction of the modification closure can often be moved outside of the loop, so imho this is not a big issue compared to running an operation unnecessarily twice.

我应该澄清我知道withDefaultValue但问题仍然存在:执行两次查找仍然比一次慢两倍,无论它是否是 O(1) 操作。坦率地说,在许多情况下,我很乐意实现因子 2 的加速。显然,修改闭包的构造通常可以移到循环之外,所以恕我直言,与运行一个不必要的操作两次。

采纳答案by Xavier Guihot

Starting Scala 2.13, Map#updateWithserves this exact purpose:

开始Scala 2.13Map#updateWith服务于这个确切的目的:

map.updateWith("a")({
  case Some(count) => Some(count + 1)
  case None        => Some(1)
})

def updateWith(key: K)(remappingFunction: (Option[V]) => Option[V]): Option[V]

def updateWith(key: K)(remappingFunction: (Option[V]) => Option[V]): Option[V]



For instance, if the key doesn't exist:

例如,如果密钥不存在:

val map = collection.mutable.Map[String, Int]()
// map: collection.mutable.Map[String, Int] = HashMap()

map.updateWith("a")({ case Some(count) => Some(count + 1) case None => Some(1) })
// Option[Int] = Some(1)
map
// collection.mutable.Map[String, Int] = HashMap("a" -> 1)

and if the key exists:

如果密钥存在:

map.updateWith("a")({ case Some(count) => Some(count + 1) case None => Some(1) })
// Option[Int] = Some(2)
map
// collection.mutable.Map[String, Int] = HashMap("a" -> 2)

回答by coltfred

You could create the map with a default value, which would allow you to do the following:

您可以使用默认值创建地图,这将允许您执行以下操作:

scala> val m = collection.mutable.Map[String, Int]().withDefaultValue(0)
m: scala.collection.mutable.Map[String,Int] = Map()

scala> m.update("a", m("a") + 1)

scala> m
res6: scala.collection.mutable.Map[String,Int] = Map(a -> 1)

As Impredicative mentioned, map lookups are fast so I wouldn't worry about 2 lookups.

正如 Impredicative 所提到的,地图查找速度很快,所以我不会担心 2 次查找。

Update:

更新:

As Debilski pointed out you can do this even more simply by doing the following:

正如 Debilski 指出的,您可以通过执行以下操作更简单地做到这一点:

scala> val m = collection.mutable.Map[String, Int]().withDefaultValue(0)
scala> m("a") += 1
scala> m
 res6: scala.collection.mutable.Map[String,Int] = Map(a -> 1)

回答by Peter L

I wanted to lazy-initialise my mutable map instead of doing a fold (for memory efficiency). The collection.mutable.Map.getOrElseUpdate()method suited my purposes. My map contained a mutable object for summing values (again, for efficiency).

我想延迟初始化我的可变映射而不是进行折叠(为了内存效率)。该collection.mutable.Map.getOrElseUpdate()方法适合我的目的。我的地图包含一个用于求和值的可变对象(再次,为了效率)。

        val accum = accums.getOrElseUpdate(key, new Accum)
        accum.add(elem.getHours, elem.getCount)

collection.mutable.Map.withDefaultValue()does not keep the default value for a subsequent requested key.

collection.mutable.Map.withDefaultValue()不会为后续请求的键保留默认值。