Есть ли SoftHashMap в Scala?

Я знаю об этом вопросе для Java, но ни одна из этих реализаций, кажется, не подходит scala.collection.JavaConversions,

Я ищу что-то простое (например, один файл, а не целую библиотеку), которое реализует SoftHashMap так что он хорошо играет со Scala Map (т.е. поддерживает getOrElseUpdate, unzipи оставшаяся Скала Map методы).

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}
}

Я нашел один в лифте.

Я еще не пользуюсь им, так что проверьте сами, пожалуйста.

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

Время от времени вам задают вопрос типа "как лучше выковырять свой глаз палкой", на который вы можете пойти на все, ответив на вопрос, как вы должны укрепить и стерилизовать палочку после вырезания 1-дюймового крючка в конце, и следовать с тем, где палка должна быть вставлена ​​и так далее. На самом деле, лучший ответ, вероятно, не совсем то, что было задано - но вопрос, почему, черт возьми, вы хотели бы сделать это в первую очередь!

Это один из тех вопросов.

SoftReferences - это то, что изначально звучит как то, что вы можете захотеть. Ссылка, которая не собирает мусор, пока не будет давление ГХ. Предположительно, вы бы использовали это для кеширования чего-то, что стоило бы кешировать, обычно потому, что изначально было дорого создавать.

Проблема в том, что SoftRef очищаются почти точно, когда вы этого не хотите, когда GC находится под давлением! Это означает, что их нужно будет воссоздать (дорогая операция) прямо тогда, когда виртуальная машина уже занята и находится под давлением GC.

Кроме того, нет способа намекнуть VM относительно приоритета объектов, на которые имеются мягкие ссылки. Конкретный алгоритм, используемый для выбора объектов для очистки, не определен и зависит от ВМ.

По сути, SoftReferences - ошибочная попытка разгрузить проблему уровня приложения (кэширование) в сборщик мусора. Вы действительно никогда не должны * фактически использовать их.

* никогда, по модулю некоторые очень маленькие и очень специализированные сценарии использования

Как заметили другие люди, SoftReferenceОбычно это не то, что нужно для создания кэша. Тем не менее, некоторые библиотеки предоставляют лучшие замены. Хотя ОП требует не использовать библиотеку, я все же думаю, что это лучший ответ. Плюс, с загрузкой 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 (а также сообщу об этом на трекере ошибок), поэтому я надеюсь, что код 2,9 по крайней мере скомпилируется в 2.10 (но все еще вызывает предупреждение об устаревании): https://groups.google.com/d/topic/scala-language/uXKRiGXb-44/discussion.

Другие вопросы по тегам