Есть ли SoftHashMap в Scala? - PullRequest
       3

Есть ли SoftHashMap в Scala?

6 голосов
/ 25 февраля 2011

Мне известен этот вопрос для java , но ни одна из этих реализаций, похоже, не подходит для scala.collection.JavaConversions.

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

Ответы [ 4 ]

4 голосов
/ 26 февраля 2011

Реализация вдохновлена ​​ этой Java WeakHashMap:

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}
}
2 голосов
/ 05 августа 2012

Как заметили другие люди, 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.

2 голосов
/ 14 декабря 2011

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

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

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

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

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

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

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

2 голосов
/ 13 декабря 2011

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

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

http://scala -tools.org / mvnsites / liftweb-2,4-M5 / нетто / liftweb / Util / SoftReferenceCache.html

...