如何使包装不可变集合的类在Scala中不可变?

时间:2016-09-19 01:39:53

标签: multithreading scala scala-collections

在之前的SO post中,我询问了一种使用容器类包装不可变集合线程安全的惯用方法。我收到的所有涉及使用各种风格的读/写锁或同步的答案,这不是我想要的。

让我问一个不同的问题。如何使包含不可变容器的以下类不可变?方法add / remove需要返回一个适当更改的新MyContainer类实例,但我不太明白该怎么做...

class MyContainer[A] {

  // method that returns a new MyContainer that includes the additional thing...
  def add(thing: A): MyContainer[A] = {
    ???
  }

  def filter(p: A => Boolean): Option[Iterable[A]] = {
    val filteredThings = backingStore.values.filter(p)
    if (filteredThings.isEmpty) None else Some(filteredThings)
  }

  // method that returns a new MyContainer that does not include the thing with given uuid
  def remove(uuid: UUID): MyContainer[A] = {
    ???
  }

  @ volatile private[this] var backingStore = immutable.HashMap.empty[UUID, A]

}

思想?

编辑:在回应评论时,一种可能的解决方案类似于以下内容......

class MyContainer[A](val backingStore: immutable.HashMap[UUID, A]) {

  def add(thing: A): MyContainer[A] = {
    new MyContainer(backingStore + (thing.uuid -> thing))
  }

  def filter(p: A => Boolean): Option[Iterable[A]] = {
    val filteredThings = backingStore.values.filter(p)
    if (filteredThings.isEmpty) None else Some(filteredThings)
  }

  def remove(uuid: UUID): MyContainer[A] = {
    new MyContainer(backingStore - uuid)
  }

}

... backingStore不再是私有的(但可以将private放在构造函数中)。更多想法?

1 个答案:

答案 0 :(得分:1)

您需要一种方法来构造一个已包含一些元素的新MyContainer,并且最好保持相同的UUID。这意味着你将基本上需要一个初始化backingStore的构造函数。但是,如果您不想以任何方式公开它,可以将构造函数设置为private,并提供一个重载的构造函数,该构造函数只允许外部代码创建一个空集合(比方说)。 backingStore可以简单地移动到私有构造函数中。

class MyContainer[A] private (backingStore: HashMap[UUID, A]) {

  def this() = this(HashMap.empty[UUID, A])

  def add(thing: A): MyContainer[A] = {
    val uuid: UUID = UUID.randomUUID() // or however the UUID is generated
    new MyContainer(backingStore + ((uuid, thing)))
  }

  def remove(uuid: UUID): MyContainer[A] =
    new MyContainer(backingStore - uuid)

}

scala> val container = new MyContainer[String]()

scala> container.add("a").add("b").add("c")
res2: MyContainer[String] = MyContainer@4a183d02

但是,您希望在API中公开这一点。我不确定你对filter的要求是什么,所以我从我的例子中删除了它。