Java中简单易用的LRU缓存


69

我知道实现起来很简单,但是我想重用已经存在的东西。

我要解决的问题是我为不同的页面,角色加载了配置(从XML,所以我想缓存它们),因此输入的组合可以增长很多(但99%的增长)。为了处理这个1%,我想在缓存中设置一些最大项目...

直到我在apache commons中找到了org.apache.commons.collections.map.LRUMap,它看起来还不错,但还想检查一下其他内容。有什么建议吗?


@Suporwski您如何解决这个问题?
2012年

@亨特,我有来自公地的LRUMap
Juraj,

Answers:


106

您可以使用LinkedHashMap(Java 1.4+):

// Create cache
final int MAX_ENTRIES = 100;
Map cache = new LinkedHashMap(MAX_ENTRIES+1, .75F, true) {
    // This method is called just after a new entry has been added
    public boolean removeEldestEntry(Map.Entry eldest) {
        return size() > MAX_ENTRIES;
    }
};

// Add to cache
Object key = "key";
cache.put(key, object);

// Get object
Object o = cache.get(key);
if (o == null && !cache.containsKey(key)) {
    // Object not in cache. If null is not a possible value in the cache,
    // the call to cache.contains(key) is not needed
}

// If the cache is to be used by multiple threads,
// the cache must be wrapped with code to synchronize the methods
cache = (Map)Collections.synchronizedMap(cache);

1
好的,所以我决定使用LRUMap。
朱拉伊,

请注意-您的两个链接都指向同一个示例,我认为前者应该是LHM javadoc?
吉比

9
我今天发现了LinkedHashMap的这一方面,它的简单性令人惊讶。获得的经验:了解您的API。
朱利安·格里尼尔

1
明确编写的示例(基于java.util.LinkedHashMap的LRU缓存类)也随提供的测试一起运行:link
Champ

负载系数真的应该是0.75吗?就像您从未真正希望Map包含超过MAX_ENTRIES个条目。
Tim P

30

这是一个老问题,但是我想列出后代 ConcurrentLinkedHashMap,它是线程安全的,与LRUMap不同。用法很简单:

ConcurrentMap<K, V> cache = new ConcurrentLinkedHashMap.Builder<K, V>()
    .maximumWeightedCapacity(1000)
    .build();

并且文档中有一些很好的例子,例如如何使LRU缓存基于大小而不是基于项目数。


6
咖啡因是Java 8重写,它更快并且提供了更多功能
本·马内斯

13

这是我的实现,可让我在内存中保留最佳数量的元素。

关键是我不需要跟踪当前正在使用的对象,因为我将MRU对象的LinkedHashMap和LRU对象的WeakHashMap结合使用。因此,缓存容量不小于MRU大小加上GC允许我保留的容量。只要有物体从MRU上掉落,它们就会进入LRU,直到GC拥有它们。

public class Cache<K,V> {
final Map<K,V> MRUdata;
final Map<K,V> LRUdata;

public Cache(final int capacity)
{
    LRUdata = new WeakHashMap<K, V>();

    MRUdata = new LinkedHashMap<K, V>(capacity+1, 1.0f, true) {
        protected boolean removeEldestEntry(Map.Entry<K,V> entry)
        {
            if (this.size() > capacity) {
                LRUdata.put(entry.getKey(), entry.getValue());
                return true;
            }
            return false;
        };
    };
}

public synchronized V tryGet(K key)
{
    V value = MRUdata.get(key);
    if (value!=null)
        return value;
    value = LRUdata.get(key);
    if (value!=null) {
        LRUdata.remove(key);
        MRUdata.put(key, value);
    }
    return value;
}

public synchronized void set(K key, V value)
{
    LRUdata.remove(key);
    MRUdata.put(key, value);
}
}

那是一个很不错的方法。因此,LRU缓存仅存储大小限制的MRU缓存中已过期的内容。就像软件二级缓存一样。真好!
Ogre Psalm15年

如果数据恰好在LRUdata中存在但在MRUdata中丢失,则可能会向应用程序日志发送有关增加缓存大小的警告/提示。
迈克

1

我也有同样的问题,但是我还没有找到任何好的库...所以我创建了自己的库。

simplelrucache提供具有TTL支持的线程安全,非常简单的非分布式LRU缓存。它提供了两种实现

  • 基于ConcurrentLinkedHashMap的并发
  • 基于LinkedHashMap同步

你可以在这里找到它。


您能在Maven Central中释放您的图书馆吗?
Jakub Jirutka

1
1.0版现在应该放在中央:)
Daimon

1

是Java中非常简单易用的LRU缓存。尽管它简短而简单,但却是生产质量。解释了该代码(请参见README.md),并进行了一些单元测试。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.