Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/17.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Scala 如何访问/初始化和更新可变映射中的值?_Scala_Dictionary - Fatal编程技术网

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

Scala 如何访问/初始化和更新可变映射中的值?,scala,dictionary,Scala,Dictionary,考虑使用可变映射跟踪事件/计数的简单问题,即: val counts = collection.mutable.Map[SomeKeyType, Int]() 我目前增加计数的方法是: counts(key) = counts.getOrElse(key, 0) + 1 // or equivalently counts.update(key, counts.getOrElse(key, 0) + 1) 这感觉有点笨拙,因为我必须指定两次键。就性能而言,我还希望键必须在地图中定位两次,这是我

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

val counts = collection.mutable.Map[SomeKeyType, Int]()
我目前增加计数的方法是:

counts(key) = counts.getOrElse(key, 0) + 1
// or equivalently
counts.update(key, counts.getOrElse(key, 0) + 1)
这感觉有点笨拙,因为我必须指定两次键。就性能而言,我还希望
必须在地图中定位两次,这是我希望避免的。有趣的是,如果
Int
提供某种机制来修改自身,那么就不会出现这种访问和更新问题。例如,将
Int
更改为提供
增量
函数的
计数器
类将允许:

// not possible with Int
counts.getOrElseUpdate(key, 0) += 1
// but with a modifiable counter
counts.getOrElseUpdate(key, new Counter).increment
不知何故,我总是希望在可变映射上具有以下功能(有点类似于
transform
,但不返回新集合,也不返回具有默认值的特定键):

然而,据我所知,这样的功能并不存在。一般来说(性能和语法方面)有这样一个
f:a=>a
就地修改的可能性不是很有意义吗?也许我只是错过了一些东西。。。我想一定有更好的办法来解决这个问题,不需要这样的功能

更新:


我应该澄清一下,我知道使用DefaultValue的
,但问题仍然是一样的:执行两次查找的速度仍然是一次查找的两倍,不管它是否是O(1)操作。坦率地说,在许多情况下,我会非常高兴地实现因子2的加速。显然,修改闭包的构造通常可以移动到循环之外,因此与不必要地运行两次操作相比,这不是一个大问题。

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

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)
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)
正如Impredicative提到的,地图查找速度很快,所以我不必担心2次查找

更新:

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

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)
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)

我想懒洋洋地初始化我的可变映射,而不是进行折叠(为了提高内存效率)。collection.mutable.Map.GetOrelsUpdate()方法适合我的目的。我的映射包含一个用于求和值的可变对象(同样,为了效率)


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

启动
Scala 2.13
,正好用于此目的:

map.updateWith("a")({
  case Some(count) => Some(count + 1)
  case None        => Some(1)
})
def updateWith(键:K)(重新映射函数:(选项[V])=>选项[V]):选项[V]


例如,如果密钥不存在:

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)

标准映射中的键查找通常应该是O(1),因此查找两次不会有太大的损失-可能比为传递到
updateorese
@Impredicative中的函数构造闭包所付出的代价要少:在本例中,这是一个很好的观点。但是特质本身的功能性并没有对此做出任何假设。例如,
Map
也由
TreeMap
ListMap
实现,它们分别是O(logn)和O(N)。因此,在不做O(1)假设的情况下,就地修改在总体上仍然是可取的。我同意你的观点,bluenote10——我确信会有类似于
map.update(key,initValue){}
的东西,因为它执行得更好,更干净。如果性能不重要,我们可能不会首先使用可变映射。正如在另一篇评论中提到的,
(+1)
不是一个闭包,因为它不在任何自由变量上闭包——没有什么可构建的。请注意,
m(“a”)+=1
m(“a”)=m(“a”)+1的糖(^)。(^或者更确切地说,按照惯例,sugar方法可能直接在
mutable.Map
上实现),我知道默认值方法,但不幸的是,我忘了在问题中提到它。我没有考虑这一点,因为我的实际关注(双重查找)并没有用这种方法解决。无论如何谢谢你!好的,抱歉误会了。创建闭包(或任何函数调用)在性能上比为HashMap进行两次映射查找要差。@coltfred:你真的确定吗?我只是快速浏览了一下
mutable.HashMap
是如何实现的。查找本身从
HashTable
调用
findEntry
,它再次调用
findEntry0
(涉及一个while循环来处理冲突)和
index
函数。
索引
计算似乎很轻,但也包含对
位计数
的函数调用。我想知道:既然一个查找本身涉及几个函数调用,它怎么可能比调用像
def(x:Int)=x+1这样的函数更快呢?我甚至不确定总的查找是否比创建一个闭包更快。只是要明确,(+1)不是一个闭包,因为它不会在任何自由变量上关闭。在调用站点上没有为其创建对象;它只是对初始化过程中一次性创建的函数的引用。请注意,问题特别针对的是
getOrElseUpdate
不适用(标量)的情况,这甚至是我在问题中给出的示例。如果你有可变的对象,这是显而易见的选择。