0
votes

I'm trying to create a Map of objects like this:

case class Worker(var name:String, var tasks:List[Int]=List())

val map = Map[Int, Worker]().withDefaultValue(Worker(""))

So that whenever a new request id (the key in the map) comes in I can create a new worker object for that id. I do not know how many request ids will come in or the range of id beforehand. But this code gives a very weird result.

scala> map.size
res34: Int = 0

scala> map(1).name = "first worker"
map(1).name: String = first worker

scala> map.size
res35: Int = 0

scala> map(3).name = "request #3"
map(3).name: String = request #3

scala> map.size
res36: Int = 0

scala> map(1).name
res37: String = request #3

It is not the result I expected. The map size is always 0. There is no new Worker objects created. I tried with mutable and immutable Maps, case class and regular class with new Worker and also tried { override def default(key:Int)=Worker("") }. Nothing works as expected. Can someone help me understand the behavior of withDefaultValue or override def default? or scala way to do this? I know I can achieve this the java way if map.containsKey(xx) map.get(xx).name = "abc" else map.put(xx, new Worker). But it is tedious.

1

1 Answers

1
votes

Accessing the default value of a Map does not add that value to the Map itself as a new value. You are simply mutating the same default value each time (same reference). It would be quite unexpected for a standard Map implementation to mutate itself.

To implement what you want, you might actually want to override the apply method to add a new element to the Map when one is not found.

import scala.collection.mutable.HashMap

case class Worker(var name: String, var tasks: List[Int] = Nil)

class RequestMap extends HashMap[Int, Worker] { self =>

    override def apply(a: Int): Worker = {
        super.get(a) match {          // Find the element through the parent implementation
            case Some(value) => value // If it exists, return it
            case None => {            
                val w = Worker("")    // Otherwise make a new one
                self += a -> w        // Add it to the Map with the desired key
                w                     // And return it
            }
        }
    }

}

This does what you want, but it is just an example of a direction you can take. It is not thread safe, and would need some sort of locking mechanism to make it so.

scala> val map = new RequestMap
map: RequestMap = Map()

scala> map(1)
res3: Worker = Worker(,List())

scala> map(2)
res4: Worker = Worker(,List())

scala> map.size
res5: Int = 2