web-dev-qa-db-fra.com

Utilisez LinkedHashMap pour implémenter le cache LRU

J'essayais d'implémenter un cache LRU à l'aide de LinkedHashMap. Dans la documentation de LinkedHashMap ( http://docs.Oracle.com/javase/7/docs/api/Java/util/LinkedHashMap.html ), il est dit:

Notez que l'ordre d'insertion n'est pas affecté si une clé est réinsérée dans la carte.

Mais quand je fais les mises suivantes

public class LRUCache<K, V> extends LinkedHashMap<K, V> {
    private int size;

    public static void main(String[] args) {
        LRUCache<Integer, Integer> cache = LRUCache.newInstance(2);
        cache.put(1, 1);
        cache.put(2, 2);
        cache.put(1, 1);
        cache.put(3, 3);

        System.out.println(cache);
    }

    private LRUCache(int size) {
        super(size, 0.75f, true);
        this.size = size;
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
        return size() > size;
    }

    public static <K, V> LRUCache<K, V> newInstance(int size) {
        return new LRUCache<K, V>(size);
    }

}

La sortie est

{1=1, 3=3}

Ce qui indique que la réinsertion a affecté la commande. Quelqu'un connaît-il une explication?

21
Lei Chen

Comme l'a souligné Jeffrey , vous utilisez accessOrder. Lorsque vous avez créé LinkedHashMap, le troisième paramètre spécifie comment l'ordre est modifié.

"true for access-order, false for insertion-order"

Pour une implémentation plus détaillée de LRU, vous pouvez consulter ceci http://www.programcreek.com/2013/03/leetcode-lru-cache-Java/

19
1736964698

Mais vous n'utilisez pas l'ordre d'insertion, vous utilisez ordre d'accès .

ordre d'itération est l'ordre dans lequel ses entrées ont été consultées pour la dernière fois, du dernier accès au plus récent (ordre d'accès)

...

L'invocation de la méthode put ou get donne accès à l'entrée correspondante

Voici donc l'état de votre cache lorsque vous le modifiez:

    LRUCache<Integer, Integer> cache = LRUCache.newInstance(2);
    cache.put(1, 1); // { 1=1 }
    cache.put(2, 2); // { 1=1, 2=2 }
    cache.put(1, 1); // { 2=2, 1=1 }
    cache.put(3, 3); // { 1=1, 3=3 }
5
Jeffrey

Voici mon implémentation en utilisant LinkedHashMap dans AccessOrder. Il déplacera le dernier élément accédé vers l'avant, ce qui n'engendre que O(1) frais généraux, car les éléments sous-jacents sont organisés dans une liste doublement liée tout en étant indexés par la fonction de hachage. Les opérations/put/top_newest_one coûtent toutes O (1).

class LRUCache extends LinkedHashMap<Integer, Integer>{
    private int maxSize;
    public LRUCache(int capacity) {
        super(capacity, 0.75f, true);
        this.maxSize = capacity;
    }

    //return -1 if miss
    public int get(int key) {
        Integer v = super.get(key);
        return v == null ? -1 : v;
    }

    public void put(int key, int value) {
        super.put(key, value);
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {
        return this.size() > maxSize; //must override it if used in a fixed cache
    }
}
3
Emilio Zhao
I also implement LRU cache with little change in code. I have tested and it works perfectly as concept of LRU cache.

package com.first.misc;
import Java.util.LinkedHashMap;
import Java.util.Map;

public class LRUCachDemo {
 public static void main(String aa[]){
     LRUCache<String, String> lruCache = new LRUCache<>(3);
     lruCache.cacheable("test", "test");
     lruCache.cacheable("test1", "test1");
     lruCache.cacheable("test2", "test2");
     lruCache.cacheable("test3", "test3");
     lruCache.cacheable("test4", "test4");
     lruCache.cacheable("test", "test");


     System.out.println(lruCache.toString());
 }
}

class LRUCache<K, T>{
    private Map<K,T> cache;
    private int windowSize;

    public LRUCache( final int windowSize) {
        this.windowSize = windowSize;
        this.cache = new LinkedHashMap<K, T>(){

            @Override
            protected boolean removeEldestEntry(Map.Entry<K, T> eldest) {
                return size() > windowSize;
            }
        };

    }


    // put data in cache
    public void cacheable(K key, T data){
        // check key is exist of not if exist than remove and again add to make it recently used
        // remove element if window size is exhaust
        if(cache.containsKey(key)){
            cache.remove(key);
        }

        cache.put(key,data);

    }

    // evict functioanlity

    @Override
    public String toString() {
        return "LRUCache{" +
                "cache=" + cache.toString() +
                ", windowSize=" + windowSize +
                '}';
    }
}
0
Rajeev Rathor

J'ai utilisé le code suivant et ses travaux !!!! J'ai pris la taille de la fenêtre à 4, mais n'importe quelle valeur peut être prise.

pour l'ordre d'insertion:
1: Vérifiez si la clé est présente.

2: Si oui, supprimez-le (en utilisant lhm.remove (clé))

3: Ajoutez la nouvelle paire de valeurs clés.

pour l'ordre d'accès:

Pas besoin de supprimer les clés, les instructions put et get font tout automatiquement.

Ce code est pour la commande d'accès:

import Java.util.LinkedHashMap;

public class LRUCacheDemo {

 public static void main(String args[]){

  LinkedHashMap<String,String> lhm = new LinkedHashMap<String,String>(4,0.75f,true) {

     @Override
     protected boolean removeEldestEntry(Map.Entry<String,String> eldest) {
         return size() > 4;
     }
 };
 lhm.put("test", "test");
 lhm.put("test1", "test1");
 lhm.put("1", "abc");
 lhm.put("test2", "test2");
 lhm.put("1", "abc");
 lhm.put("test3", "test3");
 lhm.put("test4", "test4");
 lhm.put("test3", "test3");
 lhm.put("1", "abc");
 lhm.put("test1", "test1");

 System.out.println(lhm);
}
}
0
Shrey Suri