特定のタイムアウト後に自動的にエントリを削除するJava Mapまたは同様の標準データストアを知っていますか?これは、古い期限切れのエントリが自動的に「期限切れ」になるエージングを意味します。
できれば、Mavenを介してアクセスできるオープンソースライブラリで?
私は自分で機能を実装する方法を知っており、過去に何度かそれをやったことがあるので、その点でアドバイスを求めるのではなく、良いリファレンス実装へのポインタを求めています。
WeakReferenceWeakHashMap のようなベースのソリューションはオプションではありません。キーはインターンされていない文字列である可能性が高く、ガベージコレクターに依存しない設定可能なタイムアウトが必要だからです。
Ehcache は、外部構成ファイルが必要なため、私が信頼したくないオプションです。コードのみのソリューションを探しています。
はい。 Googleコレクション、または 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);
}
});
これは、同じ要件のために行ったサンプル実装であり、並行性はうまく機能します。誰かに役立つかもしれません。
import Java.text.SimpleDateFormat;
import Java.util.Date;
import Java.util.Map;
import Java.util.concurrent.ConcurrentHashMap;
/**
*
* @author Vivekananthan M
*
* @param <K>
* @param <V>
*/
public class WeakConcurrentHashMap<K, V> extends ConcurrentHashMap<K, V> {
private static final long serialVersionUID = 1L;
private Map<K, Long> timeMap = new ConcurrentHashMap<K, Long>();
private long expiryInMillis = 1000;
private static final SimpleDateFormat sdf = new SimpleDateFormat("hh:mm:ss:SSS");
public WeakConcurrentHashMap() {
initialize();
}
public WeakConcurrentHashMap(long expiryInMillis) {
this.expiryInMillis = expiryInMillis;
initialize();
}
void initialize() {
new CleanerThread().start();
}
@Override
public V put(K key, V value) {
Date date = new Date();
timeMap.put(key, date.getTime());
System.out.println("Inserting : " + sdf.format(date) + " : " + key + " : " + value);
V returnVal = super.put(key, value);
return returnVal;
}
@Override
public void putAll(Map<? extends K, ? extends V> m) {
for (K key : m.keySet()) {
put(key, m.get(key));
}
}
@Override
public V putIfAbsent(K key, V value) {
if (!containsKey(key))
return put(key, value);
else
return get(key);
}
class CleanerThread extends Thread {
@Override
public void run() {
System.out.println("Initiating Cleaner Thread..");
while (true) {
cleanMap();
try {
Thread.sleep(expiryInMillis / 2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void cleanMap() {
long currentTime = new Date().getTime();
for (K key : timeMap.keySet()) {
if (currentTime > (timeMap.get(key) + expiryInMillis)) {
V value = remove(key);
timeMap.remove(key);
System.out.println("Removing : " + sdf.format(new Date()) + " : " + key + " : " + value);
}
}
}
}
}
Git Repo Link(リスナー実装あり)
https://github.com/vivekjustthink/WeakConcurrentHashMap
乾杯!!
Apache Commonsには、エントリを期限切れにするMapのデコレータがあります。 PassiveExpiringMap これはGuavaのキャッシュよりも簡単です。
追伸注意してください、同期されていません。
my implementation 自己有効期限付きハッシュマップを試すことができます。この実装は、スレッドを使用して期限切れのエントリを削除するのではなく、すべての操作で自動的にクリーンアップされる DelayQueue を使用します。
Googleコレクション(グアバ)には MapMaker があり、これで期限(有効期限)を設定できます。また、ファクトリメソッドを使用して選択したソフトまたは弱参照を使用して、選択したインスタンスを作成できます。
Ehcacheのように聞こえますが、あなたが望むものには過剰ですが、外部設定ファイルを必要としないことに注意してください。
一般に、構成を宣言型構成ファイルに移動することをお勧めします(したがって、新しいインストールに異なる有効期限が必要な場合に再コンパイルする必要はありません)が、まったく必要ではありません。プログラムで構成することもできます。 http://www.ehcache.org/documentation/user-guide/configuration
グアバキャッシュは実装が簡単です。グアバキャッシュを使用して、タイムベースでキーを期限切れにすることができます。私は完全に投稿を読んで、以下は私の研究の鍵を与えます。
cache = CacheBuilder.newBuilder().refreshAfterWrite(2,TimeUnit.SECONDS).
build(new CacheLoader<String, String>(){
@Override
public String load(String arg0) throws Exception {
// TODO Auto-generated method stub
return addcache(arg0);
}
}
参照: グアバキャッシュの例
誰かが簡単なものを必要とするなら、以下は簡単なキー期限切れセットです。簡単にマップに変換される場合があります。
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();
}
}
}
expiring Mapを試すことができます http://www.Java2s.com/Code/Java/Collections-Data-Structure/ExpiringMap.htm Apache MINA Projectのクラス
通常、キャッシュはオブジェクトをしばらく保持し、しばらくしてからオブジェクトを公開する必要があります。 オブジェクトを保持するのに適した時間とは、ユースケースによって異なります。私はこのことをシンプルにしたかったのです。スレッドもスケジューラもありませんでした。このアプローチは私には有効です。 SoftReference
sとは異なり、オブジェクトは一定の最短時間で使用できることが保証されています。ただし、メモリ内にとどまることはありません 太陽が赤い巨人になるまで 。
使用例として、ごく最近リクエストが行われたかどうかを確認でき、多忙なユーザーがボタンを数回押したとしてもリクエストされたアクションを2回実行しない遅い応答システムを考えてください。ただし、しばらくして同じアクションが要求された場合、再度実行されます。
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();
}
}
}
}