2008-10-22 8 views
52

Sé que es fácil de implementar, pero quiero reutilizar algo que ya existe.Caché LRU fácil de usar en java

Problema que quiero resolver es que cargo la configuración (desde XML, así que quiero almacenarlos en caché) para diferentes páginas, roles, ... por lo que la combinación de entradas puede crecer bastante (pero en 99% no) . Para manejar este 1%, quiero tener un número máximo de elementos en la memoria caché ...

Sé que he encontrado org.apache.commons.maplections.map.LRUMap en apache commons y se ve bien, pero quiero verifique también algo más. ¿Alguna recomendación?

+0

@Suporwski ¿cómo resolvió el problema? – Hunt

+0

@Hunt Tengo LRUMap de los comunes – Juraj

+0

Pregunta muy similar también [aquí] (http://stackoverflow.com/q/221525/2032064) – Mifeet

Respuesta

87

Puede utilizar un 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); 
+5

LRUMap de colecciones comunes es una buena opción también. –

+1

OK, así que decidí usar LRUMap. – Juraj

+0

Solo una nota: ambos enlaces van al mismo ejemplo, creo que el primero debería ser el LHM javadoc. – JeeBee

21

Ésta es una vieja pregunta, pero para la posteridad me quería a la lista ConcurrentLinkedHashMap, que es hilo de seguridad, a diferencia de LRUMap. El uso es muy fácil:

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

Y la documentación tiene un buen examples, como la forma de hacer que en lugar de número-de-elementos en función del tamaño de la caché de base LRU.

+5

[Cafeína] (https://github.com/ben-manes/caffeine) es la reescritura de Java 8, que es [más rápido] (https://github.com/ben-manes/caffeine/wiki/Benchmarks) y ofrece muchas más [funciones] (https://github.com/ben-manes/caffeine/wiki/Cache). –

1

También tuve el mismo problema y no encontré ninguna buena biblioteca ... así que creé la mía.

simplelrucache proporciona un almacenamiento en memoria caché de LRU no distribuido, muy simple y no distribuido con soporte TTL. Proporciona dos implementaciones

  • concurrentes basado en ConcurrentLinkedHashMap
  • sincronizada basan en LinkedHashMap

Se puede encontrar here.

+0

¿Podría publicar su biblioteca en Maven Central? –

+1

La versión 1.0 debería estar ahora en el centro :) – Daimon

1

Here es un caché LRU muy simple y fácil de usar en Java. Aunque es corto y simple, es calidad de producción. Se explica el código (consulte el archivo README.md) y tiene algunas pruebas de unidad.

11

Aquí está mi implementación que me permite mantener una cantidad óptima de elementos en la memoria.

El punto es que no necesito hacer un seguimiento de qué objetos están siendo utilizados actualmente ya que estoy usando una combinación de un LinkedHashMap para los objetos MRU y un WeakHashMap para los objetos LRU. Por lo tanto, la capacidad de caché no es menor que el tamaño de MRU y todo lo que el GC me permite conservar. Cuando los objetos se caen de la MRU van a la LRU mientras el GC los tenga.

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); 
} 
} 
+0

Ese es un enfoque ingenioso. Entonces, el caché de LRU solo almacena las cosas que ya caducaron del caché MRU de tamaño limitado. Es como un software de caché de segundo nivel. ¡Bonito! –

+0

Me tomó un minuto asimilar, ¡pero genial! – benkc

+0

Si los datos estaban presentes en LRUdata pero faltaban en MRUdata, podría ser útil advertir/indicar al registro de la aplicación sobre el aumento del tamaño de la caché. –

Cuestiones relacionadas