As other people have observed, SoftReference
s are usually not The Right Thing to build a cache. However, some libraries provide better replacements. While the OP requires not using a library, I still think this is the best answer possible. Plus, with SBT downloading the library is quite simple.
In build.sbt
, assuming you're building your project with SBT >= 0.10 (tested with 0.12), add:
libraryDependencies += "com.google.guava" % "guava" % "13.0"
libraryDependencies += "com.google.code.findbugs" % "jsr305" % "1.3.9" //Needed by guava, but marked as optional; at least Scalac 2.10 however does require it to parse annotations.
In client code, you can build your map as follows (look into CacheBuilder's option for the meaning of the various parameters; the ones below are the ones I chose for my use case):
For Scala 2.10:
import com.google.common.cache.CacheBuilder
import collection.JavaConversions._
def buildCache[K <: AnyRef, V <: AnyRef]: collection.concurrent.Map[K, V] =
CacheBuilder.newBuilder()
.maximumSize(10000).expireAfterAccess(10, TimeUnit.MINUTES)
.concurrencyLevel(2)
.build[K, V]().asMap()
For Scala 2.9 (deprecated/not compiling in 2.10):
import com.google.common.cache.CacheBuilder
import collection.{JavaConversions, mutable}
import JavaConversions._
def buildCache2_9[K <: AnyRef, V <: AnyRef]: mutable.ConcurrentMap[K, V] =
CacheBuilder.newBuilder()
.maximumSize(10000).expireAfterAccess(10, TimeUnit.MINUTES)
.concurrencyLevel(2)
.build[K, V]().asMap()
To make it work for both versions, call the implicit conversion explicitly - it's JavaConversions.asScalaConcurrentMap
. I've reported the problem on the scala-language mailing list (and will also report it on the bug tracker), so I hope that the 2.9 code will at least compile in 2.10 (while still causing deprecation warning):
https://groups.google.com/d/topic/scala-language/uXKRiGXb-44/discussion.