7

我知道java 的这个问题,但这些实现似乎都不能很好地与scala.collection.JavaConversions.

我正在寻找一些简单的东西(例如单个文件,而不是整个库),SoftHashMap以使其与 Scala 配合良好Map(即支持getOrElseUpdate,unzip和其余的 ScalaMap方法)。

4

4 回答 4

4

受此javaWeakHashMap启发的实现:

import scala.collection.mutable.{Map, HashMap}
import scala.ref._


class SoftMap[K, V <: AnyRef] extends Map[K, V]
{
  class SoftValue[K, +V <: AnyRef](val key:K, value:V, queue:ReferenceQueue[V]) extends SoftReference(value, queue)

  private val map = new HashMap[K, SoftValue[K, V]]

  private val queue = new ReferenceQueue[V]

  override def += (kv: (K, V)): this.type =
  {
    processQueue
    val sv = new SoftValue(kv._1, kv._2, queue)
    map(kv._1) = sv
    this
  }

  private def processQueue
  {
    while (true)
    {
      queue.poll match
      {   
        case Some(sv:SoftValue[K, _]) => map.remove(sv.key)
        case _ => return
      }
    }
  }


  override def get(key: K): Option[V] = map.get(key) match
  {
    case Some(sv) => sv.get match
      { case v:Some[_] => v
        case None => {map.remove(key); None} }
    case None => None
  }



  override def -=(key: K):this.type =
  {
    processQueue
    map.remove(key)
    this
  }

  override def iterator: Iterator[(K, V)] =
  {
    processQueue
    map.iterator.collect{ case (key, sv) if sv.get.isDefined => (key, sv.get.get) }
  }

  override def empty:SoftMap[K, V] = new SoftMap[K, V]

  override def size = {processQueue; map.size}
}
于 2011-02-26T18:06:36.380 回答
2

我在liftweb中找到了一个。

我还没有使用它,所以请自己检查。

http://scala-tools.org/mvnsites/liftweb-2.4-M5/net/liftweb/util/SoftReferenceCache.html

于 2011-12-13T17:49:05.723 回答
2

有时您会被问到诸如“用棍子戳眼睛的最佳方法是什么”之类的问题,您可以不遗余力地回答在末端雕刻一个 1 英寸的钩子后应该如何硬化和消毒棍子,然后按照以及应该插入棒的位置等等。但实际上,最好的答案可能并不完全是所问的——而是关于你为什么要首先这样做的问题!

这是其中一个问题。

SoftReferences 最初听起来像是您可能想要的东西。在有 GC 压力之前不会被垃圾收集的引用。大概,您会使用它来缓存值得缓存的东西,通常是因为首先创建它很昂贵。

问题是,当 GC 承受压力时,SoftRefs 几乎完全在您不希望它们时清除!这意味着当虚拟机已经很忙并且处于 GC 压力下时,需要重新创建它们(昂贵的操作)。

此外,无法向 VM 提示软引用对象的优先级。用于选择要清除的对象的特定算法未指定且取决于 VM。

从本质上讲,SoftReferences 是将应用程序级别的关注(缓存)卸载到垃圾收集器的错误尝试。你真的不应该*实际使用它们。

*从不,以一些非常小的和非常专业的用例为模

于 2011-12-14T00:58:42.870 回答
2

正如其他人所观察到的,SoftReferences 通常不是构建缓存的正确方法。但是,一些库提供了更好的替代品。虽然 OP 不需要使用库,但我仍然认为这是最好的答案。另外,使用 SBT 下载库非常简单。

build.sbt中,假设您正在使用 SBT >= 0.10(使用 0.12 测试)构建项目,请添加:

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.

在客户端代码中,您可以按如下方式构建地图(查看 CacheBuilder 的选项以了解各种参数的含义;以下是我为我的用例选择的那些):

对于 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()

对于 Scala 2.9(在 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()

要使其适用于两个版本,请显式调用隐式转换 - 它是 JavaConversions.asScalaConcurrentMap. 我已经在 scala 语言邮件列表上报告了这个问题(也会在 bug 跟踪器上报告),所以我希望 2.9 代码至少能在 2.10 中编译(同时仍然会引起弃用警告): https:// groups.google.com/d/topic/scala-language/uXKRiGXb-44/discussion

于 2012-08-04T23:47:24.327 回答