有没有一种方法可以让Scala的HashMap自动初始化值?

4
我认为可以按照以下方式完成。
val hash = new HashMap[String, ListBuffer[Int]].withDefaultValue(ListBuffer())
hash("A").append(1)
hash("B").append(2)
println(hash("B").head)

然而,上述代码输出了难以理解的值1。我希望...
hash("B").append(2)

在背后做类似以下操作的事情。
if (!hash.contains("B")) hash.put("B", ListBuffer())
2个回答

10

使用getOrElseUpdate在访问点提供默认值:

scala> import collection.mutable._
import collection.mutable._

scala> def defaultValue = ListBuffer[Int]()
defaultValue: scala.collection.mutable.ListBuffer[Int]

scala> val hash = new HashMap[String, ListBuffer[Int]]
hash: scala.collection.mutable.HashMap[String,scala.collection.mutable.ListBuffer[Int]] = Map()

scala> hash.getOrElseUpdate("A", defaultValue).append(1)

scala> hash.getOrElseUpdate("B", defaultValue).append(2)

scala> println(hash("B").head)
2

8

withDefaultValue每次都使用完全相同的值。在你的情况下,它是空的ListBuffer,这个值被所有人共享。

如果你改用withDefault,你可以每次生成一个新的ListBuffer,但它不会被存储。

因此,你真正想要的是一个方法,它知道如何添加默认值。你可以在一个包装类中创建这样的方法,然后编写一个隐式转换:

class InstantiateDefaults[A,B](h: collection.mutable.Map[A,B]) {
  def retrieve(a: A) = h.getOrElseUpdate(a, h(a))
}
implicit def hash_can_instantiate[A,B](h: collection.mutable.Map[A,B]) = {
  new InstantiateDefaults(h)
}

现在你的代码按照预期工作(除了额外的方法名称,如果需要可以选择更短的名称):

val hash = new collection.mutable.HashMap[
  String, collection.mutable.ListBuffer[Int]
].withDefault(_ => collection.mutable.ListBuffer())

scala> hash.retrieve("A").append(1)

scala> hash.retrieve("B").append(2)

scala> hash("B").head
res28: Int = 2

请注意,该解决方案(使用隐式)根本不需要知道默认值本身,因此您只需执行一次此操作,然后可以随心所欲地进行默认添加。

有没有办法覆盖 hash("B").append(2) 的默认行为? - deltanovember
3
你可以扩展 apply 方法,但很快就会变得很丑陋。在我看来,最好使用一个单独的方法。 - Rex Kerr
2
@deltanova 你可以优化 HashMap[A,ListBuffer[B]],添加一个方法 def append[A,B](key: A, value: B) = getOrElseUpdate(key)(new LB...).append(value) - ziggystar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接