2008-10-22 9 views
52

Ich weiß, es ist einfach zu implementieren, aber ich möchte etwas wiederverwenden, die bereits vorhanden sind.Einfach, einfach zu LRU-Cache in Java zu verwenden

Problem Ich möchte lösen, dass ich Konfiguration laden (aus XML, also möchte ich sie zwischenspeichern) für verschiedene Seiten, Rollen, ... so kann die Kombination von Eingaben ziemlich viel wachsen (aber in 99% wird nicht) . Um diese 1% zu behandeln, möchte ich eine maximale Anzahl von Elementen im Cache haben ...

Bis ich weiß, habe ich org.apache.commons.collections.map.LRUMap in Apache Commons gefunden und es sieht gut aus, aber wollen überprüfe auch etwas anderes. Irgendwelche Empfehlungen?

+0

@Suporwski Wie haben Sie das Problem gelöst? – Hunt

+0

@Hunt Ich habe LRUMap von commons – Juraj

+0

Sehr ähnliche Frage auch [hier] (http://Stackoverflow.com/q/221525/2032064) – Mifeet

Antwort

87

können Sie verwenden, um eine 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 aus Commons Sammlungen ist auch eine gute Wahl. –

+1

OK, also habe ich mich für LRUMap entschieden. – Juraj

+0

Nur eine Anmerkung - beide Links gehen auf das gleiche Beispiel, ich denke, das erste sollte der LHM Javadoc sein? – JeeBee

21

Dies ist eine alte Frage, aber für die Nachwelt wollte ich ConcurrentLinkedHashMap aufzulisten, was sicher ist, fädeln, im Gegensatz zu LRUMap. Die Benutzung ist ganz einfach:

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

Und die Dokumentation hat einige gute examples, wie wie die LRU-Cache-Größe Basis zu machen, anstatt Nummer-of-Artikel basiert.

+5

[Koffein] (https://github.com/ben-manes/caffeine) ist die Java 8 Neufassung, die [schneller] ist (https://github.com/ben-manes/caffeine/wiki/Benchmarks) und bietet viele weitere [Funktionen] (https://github.com/ben-manes/caffeine/wiki/Cache). –

1

Ich hatte auch das gleiche Problem und ich habe keine guten Bibliotheken gefunden ... so habe ich meine eigenen erstellt.

simplelrucache bietet threadsafe, sehr einfaches, nicht verteiltes LRU-Caching mit TTL-Unterstützung. Es bietet zwei Implementierungen

  • Concurrent basierend auf ConcurrentLinkedHashMap
  • Synchronisiert basierend auf LinkedHashMap

Sie es here finden.

+0

Könnten Sie bitte Ihre Bibliothek in Maven Central freigeben? –

+1

Version 1.0 sollte jetzt in der Mitte sein :) – Daimon

1

10 ist ein sehr einfacher und einfach zu benutzender LRU Cache in Java. Obwohl es kurz und einfach ist, ist es Produktionsqualität. Der Code wird erklärt (siehe README.md) und hat einige Komponententests.

11

Hier ist meine Implementierung, mit der ich eine optimale Anzahl von Elementen im Speicher halten kann.

Der Punkt ist, dass ich nicht verfolgen muss, welche Objekte derzeit verwendet werden, da ich eine Kombination aus einer LinkedHashMap für die MRU-Objekte und einer WeakHashMap für die LRU-Objekte verwende. Also die Cache-Kapazität ist nicht weniger als MRU-Größe plus was auch immer der GC mich behalten lässt. Immer wenn Objekte von der MRU fallen, gehen sie zur LRU, solange der GC sie hat.

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

Das ist ein geschickter Ansatz. Daher speichert der LRU-Cache nur das bereits abgelaufene Material aus dem größenbegrenzten MRU-Cache. Es ist wie ein Software-Second-Level-Cache. Nett! –

+0

Nahm mich eine Minute zu grok, aber sehr cool! – benkc

+0

Wenn Daten in LRUdata vorhanden sind, aber in MRUdata fehlen, kann es nützlich sein, das Anwendungsprotokoll über die Erhöhung der Cachegröße zu warnen. –