4

I thought it could be done as follows

val hash = new HashMap[String, ListBuffer[Int]].withDefaultValue(ListBuffer())
hash("A").append(1)
hash("B").append(2)
println(hash("B").head)

However the above prints the unintuitive value of 1. I would like

hash("B").append(2)

To do something like the following behind the scenes

if (!hash.contains("B")) hash.put("B", ListBuffer())

2 Answers 2

10

Use getOrElseUpdate to provide the default value at the point of access:

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
Sign up to request clarification or add additional context in comments.

Comments

8

withDefaultValue uses exactly the same value each time. In your case, it's the same empty ListBuffer that gets shared by everyone.

If you use withDefault instead, you could generate a new ListBuffer every time, but it wouldn't get stored.

So what you'd really like is a method that would know to add the default value. You can create such a method inside a wrapper class and then write an implicit conversion:

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

Now your code works as desired (except for the extra method name, which you could pick to be shorter if you wanted):

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

Note that the solution (with the implicit) doesn't need to know the default value itself at all, so you can do this once and then default-with-addition to your heart's content.

3 Comments

Is there some way to overwrite the default behaviour of hash("B").append(2)?
You can extend apply, but that gets ugly quickly. You're better off using a separate method IMO.
@deltanova You can pimp HashMap[A,ListBuffer[B]] to have a method def append[A,B](key: A, value: B) = getOrElseUpdate(key)(new LB...).append(value).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.