web-dev-qa-db-fra.com

Facile, simple à utiliser le cache LRU dans java

Je sais que c'est simple à implémenter, mais je veux réutiliser quelque chose qui existe déjà.

Le problème que je veux résoudre est que je charge la configuration (à partir de XML, donc je veux les mettre en cache) pour différentes pages, rôles, ... de sorte que la combinaison des entrées peut augmenter considérablement (mais pas dans 99% des cas). Pour gérer ce 1%, je veux avoir un nombre maximum d'éléments dans le cache ...

Jusqu'à ce que je sache que j'ai trouvé org.Apache.commons.collections.map.LRUMap dans Apache commons et cela semble bien mais je veux vérifier aussi autre chose. Des recommandations?

62
Juraj

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);
98
Guido

C'est une vieille question, mais pour la postérité, je voulais lister ConcurrentLinkedHashMap , qui est thread-safe, contrairement à LRUMap . L'utilisation est assez simple:

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

Et la documentation a quelques bons exemples , comme comment faire le cache LRU en fonction de la taille au lieu du nombre d'éléments.

27
Bobby Powers

Voici mon implémentation qui me permet de garder un nombre optimal d'éléments en mémoire.

Le fait est que je n'ai pas besoin de garder une trace des objets actuellement utilisés car j'utilise une combinaison d'un LinkedHashMap pour les objets MRU et d'un WeakHashMap pour les objets LRU. La capacité du cache n'est donc pas inférieure à la taille de la MRU plus 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);
}
}
13
botek

J'ai également eu le même problème et je n'ai pas trouvé de bonnes bibliothèques ... alors j'ai créé la mienne.

simplelrucache fournit une mise en cache LRU non-distribuée, très simple et non distribuée avec prise en charge de TTL. Il fournit deux implémentations

  • Concurrent basé sur ConcurrentLinkedHashMap
  • Synchronisé basé sur LinkedHashMap

Vous pouvez le trouver ici .

1
Daimon

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

1
Barak