考虑一下使用可变映射来跟踪事件/计数的简单问题,即:

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/

10-09 20:21