在Scala中实现findOrCreate池的最有效方法是什么?

时间:2013-12-12 06:01:47

标签: scala hashcode equality

我想创建一个类型的对象池,比如Person,实现如下:

class Person(val name: String, val email: String) { 
  val data = expensiveComputation(name, email)
}

并有一个如下所示的查找方法:

def findOrCreate(name: String, email: String): Person

此方法应该具有以下属性:它始终将给定参数集的同一对象返回到findOrCreate,如果它不存在则创建它。此外,我希望这些对象能够进行廉价的平等测试和散列。

初始化这些对象时,需要进行一些昂贵的计算,所以我真的想避免两次实例化一个对象。此外,如果我没有查找工具,域(实际上是生物信息学中的序列处理)将使我将每个对象实例化10-100次,因此内存成本是另一个令人信服的理由。

我想到的最好的方法是创建一个mutable HashMap(以避免GC开销,因为我将创建大量对象),将参数映射到Person实例,并检查是否存在每次通话都会映射。此外,我会通过调用eq来覆盖等于,并将hashCode保留为默认的AnyRef.hashCode实现。

有没有更好的方法来实现这种模式?

2 个答案:

答案 0 :(得分:3)

我认为你应该写你的课

case class Person(name: String, email: String)

你可以免费获得平等和散列,因为它是一个案例类。

我认为你真的不需要为此维护一个HashMap。处理HashMap,更新它,确保它是线程安全的并且始终处于一致状态,回收未使用的实例等的开销对于具有两个字符串参数的case类可能是不值得的。

问题编辑后编辑:使用案例类不会受到影响,您可以免费获得equalshashCode,但如果您最终使用Map[(String, String), Person],则不会需要它,因为你有一个键的字符串元组。使用HashMap很好,但正如我之前提到的:确保你的代码是线程安全的(除非让thread1和thread2创建Person(“bla”,“bla”)不是问题,你仍然会得到结果只有一个实例存储在Map中)你可能还应该处理到期以避免可能的内存泄漏。我会探索一个真正的缓存解决方案,如 Guava 库(或一些scala等价物)中提供的解决方案。

答案 1 :(得分:1)

从设计的角度来看,您希望创建一个配套对象并隐藏findOrCreate方法背后的apply功能。这将使您在使用内部缓存时创建人物对象。

对于实际缓存,可变映射就足够了,但也可以使用Java或Guava等价物。

case class Person(name: String, email: String) 

object Person {

  private lazy val cache = collection.mutable.Map[String, Person]()

  def apply( name: String, email: String): Person = {
     cache.getOrElseUpdate( (name+email).toLowerCase, createPerson(name,email))
  } 

  private def createPerson( name: String, email: String): Person = {
     // long operation here
  }
}

然后让你的人看起来像

   Person( name: String, email: String)