我想创建一个类型的对象池,比如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
实现。
有没有更好的方法来实现这种模式?
答案 0 :(得分:3)
我认为你应该写你的课
case class Person(name: String, email: String)
你可以免费获得平等和散列,因为它是一个案例类。
我认为你真的不需要为此维护一个HashMap。处理HashMap,更新它,确保它是线程安全的并且始终处于一致状态,回收未使用的实例等的开销对于具有两个字符串参数的case类可能是不值得的。
问题编辑后编辑:使用案例类不会受到影响,您可以免费获得equals
和hashCode
,但如果您最终使用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)