如何在scala中将默认地图值设置为新对象?

时间:2016-03-08 03:41:35

标签: scala

我正在尝试创建这样的对象地图:

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

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

因此,每当有新的请求id(地图中的键)进来时,我就可以为该id创建一个新的worker对象。我不知道会有多少请求id或id的范围。但是这段代码给出了一个非常奇怪的结果。

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

这不是我预期的结果。地图大小始终为0.没有创建新的Worker对象。我尝试使用可变和不可变的地图,案例类和常规类与新工作者,并尝试{ override def default(key:Int)=Worker("") }。什么都没有按预期工作。有人可以帮我理解withDefaultValue的行为或覆盖def默认值吗?或scala方式这样做?我知道我可以用java方式实现这个if map.containsKey(xx) map.get(xx).name = "abc" else map.put(xx, new Worker)。但这很乏味。

1 个答案:

答案 0 :(得分:1)

访问Map的默认值不会将该值作为新值添加到Map本身。您只是每次都改变相同的默认值(相同的参考)。对于标准Map实现来说,改变自己是非常意外的。

要实现您想要的功能,您实际上可能希望覆盖apply方法,以便在找不到新元素时向Map添加新元素。

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

}

这可以满足您的需求,但它只是您可以采取的方向示例。它不是线程安全的,并且需要某种锁定机制来实现它。

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