考虑一下使用可变映射来跟踪事件/计数的简单问题,即:
val counts = collection.mutable.Map[SomeKeyType, Int]()
我当前增加计数的方法是:
counts(key) = counts.getOrElse(key, 0) + 1
// or equivalently
counts.update(key, counts.getOrElse(key, 0) + 1)
这有点笨拙,因为我必须两次指定密钥。在性能方面,我还希望
key
在 map 中必须位于两次,这是我想避免的。有趣的是,如果Int
提供某种修改自身的机制,则不会发生此访问和更新问题。从Int
更改为提供Counter
函数的increment
类将例如允许:// not possible with Int
counts.getOrElseUpdate(key, 0) += 1
// but with a modifiable counter
counts.getOrElseUpdate(key, new Counter).increment
我总是以某种方式希望具有可变映射的以下功能(有点类似于
transform
,但不返回新集合,并且具有默认值的特定键):// fictitious use
counts.updateOrElse(key, 0, _ + 1)
// or alternatively
counts.getOrElseUpdate(key, 0).modify(_ + 1)
但是据我所知,这种功能不存在。一般而言(在性能和语法上)具有这种
f: A => A
就地修改可能性是否有意义?可能我只是在这里遗漏了一些东西……我想必须有一些更好的解决方案来解决这个问题,而使这种功能变得不必要了?更新:
我应该澄清一下,我知道
withDefaultValue
,但是问题仍然存在:无论是否为O(1)操作,执行两次查找的速度仍然是一次的两倍。坦率地说,在很多情况下,我很高兴实现因子2的加速。很明显,修改闭包的构造通常可以移到循环之外,因此,与运行操作不必要地两次。 最佳答案
从Scala 2.13
开始, Map#updateWith
达到了这个确切目的:
map.updateWith("a")({
case Some(count) => Some(count + 1)
case None => Some(1)
})
例如,如果密钥不存在:
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)
以及密钥是否存在:
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)
关于scala - 如何访问/初始化和更新可变映射中的值?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/15505048/