Mappa / cache basata sul tempo Java con chiavi in ​​scadenza

Qualcuno di voi sa di una mappa Java o di un archivio dati standard simile che cancella automaticamente le voci dopo un determinato timeout? Questo significa invecchiamento, in cui le vecchie voci scadute “si estinguono” automaticamente.

Preferibilmente in una libreria open source accessibile tramite Maven?

Conosco i modi per implementare personalmente la funzionalità e l’ho fatto più volte in passato, quindi non sto chiedendo consigli al riguardo, ma per i riferimenti a una buona implementazione di riferimento.

Le soluzioni basate su WeakReference come WeakHashMap non sono un’opzione, perché è probabile che le mie chiavi siano stringhe non internate e voglio un timeout configurabile che non dipenda dal garbage collector.

Ehcache è anche un’opzione su cui non vorrei fare affidamento perché ha bisogno di file di configurazione esterni. Sto cercando una soluzione solo per il codice.

Sì. Google Collections, o Guava come ora ha qualcosa chiamato MapMaker che può fare esattamente questo.

ConcurrentMap graphs = new MapMaker() .concurrencyLevel(4) .softKeys() .weakValues() .maximumSize(10000) .expiration(10, TimeUnit.MINUTES) .makeComputingMap( new Function() { public Graph apply(Key key) { return createExpensiveGraph(key); } }); 

Aggiornare:

A partire da guava 10.0 (rilasciato il 28 settembre 2011) molti di questi metodi MapMaker sono stati deprecati a favore del nuovo CacheBuilder :

 LoadingCache graphs = CacheBuilder.newBuilder() .maximumSize(10000) .expireAfterWrite(10, TimeUnit.MINUTES) .build( new CacheLoader() { public Graph load(Key key) throws AnyException { return createExpensiveGraph(key); } }); 

Questa è un’implementazione di esempio che ho fatto per lo stesso requisito e la concorrenza funziona bene. Potrebbe essere utile per qualcuno.

 import java.text.SimpleDateFormat; import java.util.Date; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * * @author Vivekananthan M * * @param  * @param  */ public class WeakConcurrentHashMap extends ConcurrentHashMap { private static final long serialVersionUID = 1L; private Map timeMap = new ConcurrentHashMap(); 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 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); } } } } } 

Saluti!!

Puoi provare la mia implementazione di una mappa hash in scadenza automatica. Questa implementazione non utilizza i thread per rimuovere le voci scadute, ma utilizza DelayQueue che viene ripulito automaticamente ad ogni operazione.

Apache Commons ha un decoratore per cui Map espira gli enti: PassiveExpiringMap È più semplice delle cache di Guava.

PS stai attento, non è sincronizzato.

Le raccolte di Google (guava) hanno MapMaker in cui è ansible impostare il limite di tempo (per scadenza) e puoi utilizzare riferimenti morbidi o deboli quando si sceglie di utilizzare un metodo di fabbrica per creare istanze di propria scelta.

Sembra che ehcache sia eccessivo per quello che vuoi, tuttavia nota che non ha bisogno di file di configurazione esterni.

In genere è consigliabile spostare la configurazione in file di configurazione dichiarativi (quindi non è necessario ricompilare quando una nuova installazione richiede una diversa scadenza), ma non è affatto necessaria, è comunque ansible configurarla a livello di programmazione. http://www.ehcache.org/documentation/user-guide/configuration

La cache di guava è facile da implementare. Scadiamo le chiavi in ​​base ai tempi usando la cache di guava. Ho letto completamente post e qui di seguito è la chiave del mio studio.

 cache = CacheBuilder.newBuilder().refreshAfterWrite(2,TimeUnit.SECONDS). build(new CacheLoader(){ @Override public String load(String arg0) throws Exception { // TODO Auto-generated method stub return addcache(arg0); } } 

Riferimento: esempio di cache guava

puoi provare Expiring Map http://www.java2s.com/Code/Java/Collections-Data-Structure/ExpiringMap.htm una lezione da The Apache MINA Project

In genere, una cache dovrebbe conservare gli oggetti per un certo periodo di tempo e deve esporli un po ‘di tempo dopo. Qual è il momento giusto per tenere un object dipende dal caso d’uso. Volevo che questa cosa fosse semplice, senza discussioni o scheduler. Questo approccio funziona per me. A differenza di SoftReference s, gli oggetti sono garantiti per essere disponibili una quantità minima di tempo. Tuttavia, il non rimanere in memoria fino a quando il sole si trasforma in un gigante rosso .

Come esempio di utilizzo, pensiamo a un sistema a risposta lenta che deve essere in grado di verificare se una richiesta è stata eseguita abbastanza di recente, e in tal caso non eseguire l’azione richiesta due volte, anche se un utente frenetico colpisce più volte il pulsante. Ma, se la stessa azione viene richiesta qualche tempo dopo, deve essere eseguita di nuovo.

 class Cache { long avg, count, created, max, min; Map map = new HashMap(); /** * @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> 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(); } } } } 

Se qualcuno ha bisogno di una cosa semplice, di seguito è un semplice set di scadenza della chiave. Potrebbe essere facilmente convertito in una mappa.

 public class CacheSet { public static final int TIME_OUT = 86400 * 1000; LinkedHashMap linkedHashMap = new LinkedHashMap() { @Override protected boolean removeEldestEntry(Map.Entry eldest) { final long time = System.currentTimeMillis(); if( time - eldest.getValue().time > TIME_OUT) { Iterator 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(); } } }