带有var或Mutable映射的不可变映射与val?

时间:2018-01-11 06:44:07

标签: scala concurrency immutability scala-collections mutable

以下哪两个片段效率更高,行为正确?

// snippet 1
var map = Map[String, Int]()   // immutable map with var
map.synchronized(map += "hello" -> 1)
map.synchronized(map -= "hello")

// snippet 2
val mmap = scala.collection.mutable.Map[String, Int]()
mmap.synchronized(mmap += "hello" -> 1)
mmap.synchronized(mmap -= "hello")
编辑:我正在查看并发访问地图的情况,其中多个不同的actor共享同一个地图并想要修改它。此外,相关问题还涉及varval的一般情况,而我需要对Map集合类型有所了解。

2 个答案:

答案 0 :(得分:1)

这取决于可变对象和不可变对象都有他们的专业和对象。

不可变对象使并发编程更容易,更安全,您可以轻松推理它们。围绕JVM并发发生的大多数运行时错误都是由于共享的可变状态。

如果您的对象变大,仅仅为了维持不可变状态而复制对象是没有意义的。在设计算法时,你必须明智地思考。

答案 1 :(得分:0)

  

soote:有没有理由你不能把这个地图放在一个演员里面,让其他演员使用消息来修改/阅读它?

     

Jus12:@soote再想一想,你是对的!我将使用适当的actor模型来实现它。

实际上,soote是正确的:因为你显然使用的是演员系统,所以地图应该存在于演员中。通常,当在actor中表示可变状态时,应该使用具有var的不可变数据结构,而不是具有val的可变数据结构。这样做的原因是为了防止该状态泄漏到演员的边界之外。以下是使用Akka的具体示例:

case object GetState
case class Add(key: String, value: Int)
case class Remove(key: String)

class MyActor extends Actor {
  val state = mutable.Map[String, Int]()

  def receive = {
    case GetState =>
      sender ! state
    case Add(k, v) =>
      state += (k -> v)
    case Remove(k) =>
      state -= k
  }
}

当上述演员收到GetState消息时,它会将其内部地图发送给发件人。由于地图是可变的,因此发送者现在能够从演员之外修改该地图,从而允许破坏演员状态的可能性。为了防止这种泄漏,限制对actor本身的可变性:

class MyActor extends Actor {
  var state = Map[String, Int]() // immutable map

  def receive = {
    case GetState =>
      sender ! state
    case Add(k, v) =>
      state = state + (k -> v)
    case Remove(k) =>
      state = state - k
  }
}

现在MyActor可以安全地将其状态发送给其他actor,因为它的状态是不可变的映射。