2008-10-22 5 views
52

Je sais que c'est simple à mettre en œuvre, mais je veux réutiliser quelque chose qui existe déjà. Problème Je veux résoudre est que je charge la configuration (du XML ainsi je veux les mettre en cache) pour différentes pages, rôles, ... ainsi la combinaison des entrées peut grandir assez (mais dans 99% ne le fera pas) . Pour gérer ce 1%, je veux avoir un certain nombre max d'éléments dans le cache ...Simple, facile à utiliser cache LRU dans Java

Jusqu'à ce que j'ai trouvé org.apache.commons.collections.map.LRUMap dans apache commons et ça semble bien, mais je veux vérifiez aussi quelque chose d'autre. Des recommandations?

+0

@Suporwski Comment avez-vous résolu le problème? – Hunt

+0

@Hunt J'ai LRUMap de commons – Juraj

+0

Question très similaire aussi [ici] (http://stackoverflow.com/q/221525/2032064) – Mifeet

Répondre

87

Vous pouvez utiliser 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 des collections de communes est également un bon choix. –

+1

OK, j'ai donc décidé d'utiliser LRUMap. – Juraj

+0

Juste une note - vos deux liens vont au même exemple, je pense que le premier devrait être le javadoc LHM? – JeeBee

21

Ceci est une question ancienne, mais pour la postérité que je voulais énumérer ConcurrentLinkedHashMap, qui est thread-safe, contrairement à LRUMap. L'utilisation est assez facile:

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

Et la documentation a quelque chose de bon examples, comme la façon de faire de la taille en fonction de cache LRU au lieu de numéro de-éléments en fonction.

+5

[Caféine] (https://github.com/ben-manes/caffeine) est la réécriture Java 8, qui est [plus rapide] (https://github.com/ben-manes/caffeine/wiki/Benchmarks) et offre beaucoup plus [fonctionnalités] (https://github.com/ben-manes/caffeine/wiki/Cache). –

1

J'ai aussi eu le même problème et je n'ai pas trouvé de bonnes bibliothèques ... alors j'ai créé le mien. Simplelrucache fournit une mise en cache LRU non distribuée et très simple à mettre en œuvre, avec un support TTL. Il fournit deux implémentations

  • simultanées basé sur ConcurrentLinkedHashMap
  • Synchronisé basé sur LinkedHashMap

Vous pouvez trouver here.

+0

Pourriez-vous s'il vous plaît libérer votre bibliothèque dans Maven Central? –

+1

Version 1.0 devrait être maintenant dans le centre :) – Daimon

1

Here est un cache LRU très simple et facile à utiliser en Java. Bien qu'il soit court et simple, c'est la qualité de production. Le code est expliqué (regardez le fichier README.md) et comporte quelques tests unitaires.

11

Voici ma mise en œuvre qui me permet de garder un nombre optimal d'éléments en mémoire. Le point est que je n'ai pas besoin de garder trace des objets actuellement utilisés puisque j'utilise une combinaison d'un LinkedHashMap pour les objets MRU et un WeakHashMap pour les objets LRU. Donc, la capacité de cache n'est pas inférieure à la taille MRU plus tout ce que le GC me permet de conserver. Chaque fois que des objets tombent du MRU, ils vont au LRU aussi longtemps que le GC les aura.

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

C'est une approche astucieuse. Le cache LRU stocke donc les éléments déjà expirés du cache MRU limité en taille. C'est comme un cache de second niveau. Agréable! –

+0

Il m'a fallu une minute pour grok, mais très cool! – benkc

+0

Si des données se trouvaient dans LRUdata mais manquaient dans MRUdata, il pourrait être utile d'avertir/d'avertir le journal des applications de l'augmentation de la taille du cache. –

Questions connexes