LRUCache in Scala?

2022-09-04 08:32:42

我知道番石榴有一个优秀的缓存库,但我正在寻找一些更斯卡拉/功能友好的东西,在那里我可以做这样的事情。我也看了Scalaz的备忘录,但没有lru过期。cache.getOrElse(query, { /* expensive operation */})


答案 1

Spray人员有一个使用Futures的spray-caching模块。有一个普通的LRU版本和一个版本,允许您指定一个明确的生存时间,之后条目将自动过期。

期货的使用显然允许你编写不阻塞的代码。然而,真正酷的是,它解决了“雷鸣般的牛群”问题作为奖励。例如,假设一堆请求同时传入不在缓存中的同一条目。在朴素的缓存实现中,一百个线程可能会在缓存中的该条目上出错,然后运行以为该缓存条目生成相同的数据,但当然99%只是浪费了精力。您真正想要的是只有一个线程生成数据,并且所有100个请求者都可以看到结果。如果您的缓存包含 Futures,这种情况会很自然地发生:第一个请求者会立即在缓存中安装 Future,因此只有第一个请求者会错过。所有 100 个请求者都将获得生成结果的未来。


答案 2

LRUCache解决方案基于Java LinkedHashMap,并公开为Scala可变。地图

import java.util.Collections.synchronizedMap

import scala.collection.JavaConversions._
import scala.collection.mutable

class LRUCache[K, V](maxEntries: Int)
  extends java.util.LinkedHashMap[K, V](100, .75f, true) {

  override def removeEldestEntry(eldest: java.util.Map.Entry[K, V]): Boolean 
     = size > maxEntries

}

object LRUCache {
  def apply[K, V](maxEntries: Int): mutable.Map[K, V] 
    = synchronizedMap(new LRUCache[K, V](maxEntries))
}

当地图大小> maxEntries 最后使用的条目将被删除。

对于 LRU 策略,LinkedHashMap 第 3 个构造函数参数应设置为 true。LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder)

用法示例:

val cache = LRUCache[String, Int](1000)
val key = "key1"
val value = 111

cache.get(key) shouldBe None
cache += key -> value
cache.get(key) shouldBe Some(value)
cache(key) shouldBe value