我知道withDefault
允许您指定一个函数,该函数将应用于键以生成默认的映射值,但每次将未知键传递到地图时都会调用该函数,即使您传递相同的密钥 - 值不会存储在地图中。
scala> case class Foo(foo: String) {}
defined class Foo
scala> val m = mutable.OpenHashMap[String, Foo]().withDefault( s => {
| println(s"creating new value for $s")
| Foo(s)
| })
m: scala.collection.mutable.Map[String,Foo] = Map()
scala> m("elvis")
creating value for elvis
res1: Foo = Foo(elvis)
scala> m("elvis")
creating value for elvis
res2: Foo = Foo(elvis)
现在显然这对案例类来说并不是什么大不了的事,但如果这些值本身就是可变集合,或者任何其他可变对象,那么它并不是很有用。无论我在何处访问地图,我都会使用getOrElseUpdate
。当然,这是一个更优雅的解决方案吗?
或者,因为在这种情况下,&#39;默认&#39;函数实际上是唯一的方式我将生成地图值,是否有一个优雅的,类似Scala的解决方案,用于缓存函数返回值并不涉及可变映射?< / p>
ETA:为了清楚起见,我确实想为每个不同的键再次调用该函数。但是对于给定的键,我想只调用一次该函数,并在下次返回相同的对象。
答案 0 :(得分:4)
Scalas mutable.Map
实际上是设计为子类,因此可以覆盖default
。默认default
只会抛出NoSuchElementException
,而现有的withDefault(Value)
方法只会覆盖default
。您可以在缓存值时执行相同的操作。
scala> import scala.collection.mutable
import scala.collection.mutable
scala> case class Foo(foo: String)
defined class Foo
scala> val m = new mutable.OpenHashMap[String, Foo] {
| override def default(key: String): Foo = {
| println(s"creating new value for $key")
| val foo = Foo(key)
| put(key, foo)
| foo
| }
| }
m: scala.collection.mutable.OpenHashMap[String,Foo] = OpenHashMap()
scala> m("elvis")
creating new value for elvis
res0: Foo = Foo(elvis)
scala> m("elvis")
res1: Foo = Foo(elvis)
scala> m("not elvis")
creating new value for not elvis
res2: Foo = Foo(not elvis)
答案 1 :(得分:0)
试试这个解决方案。它需要改进。但主要思想包括:
case class Foo(foo: String) {}
implicit class MapExt[K, V](m: mutable.Map[K, V]) {
def withCachedDefault(f: K => V): mutable.Map[K, V] = {
new mutable.Map[K, V] { // create wrapper around 'm'
override def apply(k: K): V = this.get(k) match {
case None =>
val v: V = f(k)
m.put(k, v) // caching new non-existing value
v
case Some(v) => v
}
override def get(key: K): Option[V] = m.get(key)
override def iterator: Iterator[(K, V)] = m.iterator
override def +=(kv: (K, V)): this.type = ??? /* TODO wrap m.+= */
override def -=(key: K): this.type = ??? /* TODO wrap m.-= */
}
}
}
val m = mutable.OpenHashMap[String, Foo]().withCachedDefault(s => {
println(s"creating new value for $s")
Foo(s)
})
println(m("elvis"))
println(m("elvis"))
println(m("word"))
println(m("word"))
// output:
//> creating new value for elvis
//> Foo(elvis)
//> Foo(elvis)
//> creating new value for word
//> Foo(word)
//> Foo(word)
该解决方案存在一些需要改进的问题。例如,可变状态的不可预测性:
val m = mutable.OpenHashMap[String, Foo]().withCachedDefault(fun1)
val m2 = m.withCachedDefault(fun2)
m2("elvis")
m2("new value")
等等。