有人知道在给定超时后自动清除条目的Java Map或类似的标准数据存储吗?这意味着老化,旧的过期条目会自动“老化”。
我知道自己实现这个功能的方法,过去也做过几次,所以我不是在寻求这方面的建议,而是寻求一个好的参考实现的指针。
基于WeakReference的解决方案(如WeakHashMap)不是一个选项,因为我的键很可能是非被驻留的字符串,而且我想要一个不依赖于垃圾收集器的可配置超时。
Ehcache也是一个我不想依赖的选项,因为它需要外部配置文件。我正在寻找一个只有代码的解决方案。
有人知道在给定超时后自动清除条目的Java Map或类似的标准数据存储吗?这意味着老化,旧的过期条目会自动“老化”。
我知道自己实现这个功能的方法,过去也做过几次,所以我不是在寻求这方面的建议,而是寻求一个好的参考实现的指针。
基于WeakReference的解决方案(如WeakHashMap)不是一个选项,因为我的键很可能是非被驻留的字符串,而且我想要一个不依赖于垃圾收集器的可配置超时。
Ehcache也是一个我不想依赖的选项,因为它需要外部配置文件。我正在寻找一个只有代码的解决方案。
当前回答
是的。谷歌集合,或者叫Guava现在有一个叫MapMaker的东西可以做到这一点。
ConcurrentMap<Key, Graph> graphs = new MapMaker()
.concurrencyLevel(4)
.softKeys()
.weakValues()
.maximumSize(10000)
.expiration(10, TimeUnit.MINUTES)
.makeComputingMap(
new Function<Key, Graph>() {
public Graph apply(Key key) {
return createExpensiveGraph(key);
}
});
更新:
在guava 10.0(发布于2011年9月28日)中,许多MapMaker方法已经被弃用,取而代之的是新的CacheBuilder:
LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()
.maximumSize(10000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build(
new CacheLoader<Key, Graph>() {
public Graph load(Key key) throws AnyException {
return createExpensiveGraph(key);
}
});
其他回答
通常,缓存应该将对象保存一段时间,并在一段时间后将它们公开。什么时候是保存对象的好时机取决于用例。我希望这件事很简单,没有线程或调度器。这种方法对我很有效。与softreference不同的是,对象保证在最短时间内可用。然而,在太阳变成红巨星之前,它们不会停留在记忆中。
作为使用示例,请考虑一个响应缓慢的系统,它应该能够检查一个请求是否最近已经完成,并且在这种情况下,即使繁忙的用户多次按下按钮,也不会执行两次所请求的操作。但是,如果一段时间后要求进行相同的操作,则应再次执行。
class Cache<T> {
long avg, count, created, max, min;
Map<T, Long> map = new HashMap<T, Long>();
/**
* @param min minimal time [ns] to hold an object
* @param max maximal time [ns] to hold an object
*/
Cache(long min, long max) {
created = System.nanoTime();
this.min = min;
this.max = max;
avg = (min + max) / 2;
}
boolean add(T e) {
boolean result = map.put(e, Long.valueOf(System.nanoTime())) != null;
onAccess();
return result;
}
boolean contains(Object o) {
boolean result = map.containsKey(o);
onAccess();
return result;
}
private void onAccess() {
count++;
long now = System.nanoTime();
for (Iterator<Entry<T, Long>> it = map.entrySet().iterator(); it.hasNext();) {
long t = it.next().getValue();
if (now > t + min && (now > t + max || now + (now - created) / count > t + avg)) {
it.remove();
}
}
}
}
Apache Commons有用于Map过期条目的装饰器:PassiveExpiringMap 这比番石榴的贮藏物简单多了。
另外,小心点,这不是同步的。
如果有人需要一个简单的东西,下面是一个简单的键到期集。它可以很容易地转换为地图。
public class CacheSet<K> {
public static final int TIME_OUT = 86400 * 1000;
LinkedHashMap<K, Hit> linkedHashMap = new LinkedHashMap<K, Hit>() {
@Override
protected boolean removeEldestEntry(Map.Entry<K, Hit> eldest) {
final long time = System.currentTimeMillis();
if( time - eldest.getValue().time > TIME_OUT) {
Iterator<Hit> i = values().iterator();
i.next();
do {
i.remove();
} while( i.hasNext() && time - i.next().time > TIME_OUT );
}
return false;
}
};
public boolean putIfNotExists(K key) {
Hit value = linkedHashMap.get(key);
if( value != null ) {
return false;
}
linkedHashMap.put(key, new Hit());
return true;
}
private static class Hit {
final long time;
Hit() {
this.time = System.currentTimeMillis();
}
}
}
你可以试试过期地图 http://www.java2s.com/Code/Java/Collections-Data-Structure/ExpiringMap.htm 一个来自Apache MINA项目的类
听起来ehcache对你想要的东西来说有点过头了,但是请注意它不需要外部配置文件。
将配置移动到声明性配置文件中通常是一个好主意(这样当新安装需要不同的过期时间时,您就不需要重新编译),但这根本不是必需的,您仍然可以以编程方式配置它。http://www.ehcache.org/documentation/user-guide/configuration