146. Mecanismo de caché LRU (lru-cache) -La versión de implementación de LinkedHashMap es súper simple

146. Mecanismo de almacenamiento en caché de LRU

Utilice la estructura de datos que conoce para diseñar e implementar una LRU (最近最少使用) 缓存机制. Debe admitir las siguientes operaciones: obtener datos, obtener y escribir datos, poner.

Obtener datos: get(key)si la clave existe en la caché, obtenga el valor de la clave (siempre un número positivo), de lo contrario, regrese -1.
Escribir datos: put(key, value)si la palabra clave ya existe, cambie su valor de datos; si la palabra clave no existe, inserte el conjunto de "palabra clave / valor". Cuando la capacidad de la caché alcanza el límite superior, debe eliminar el valor de datos no utilizado más antiguo antes de escribir nuevos datos para dejar espacio para el nuevo valor de datos.

Avanzado:

¿Puede completar estas dos operaciones en O (1) complejidad de tiempo?

Ejemplo:

LRUCache cache = new LRUCache( 2 /* 缓存容量 */ );

cache.put(1, 1);
cache.put(2, 2);
cache.get(1);       // 返回  1
cache.put(3, 3);    // 该操作会使得关键字 2 作废
cache.get(2);       // 返回 -1 (未找到)
cache.put(4, 4);    // 该操作会使得关键字 1 作废
cache.get(1);       // 返回 -1 (未找到)
cache.get(3);       // 返回  3
cache.get(4);       // 返回  4

Codigo 1

LinkedHashMap mantiene una lista doblemente vinculada de todas las entidades internamente

Al mismo tiempo, cuando el constructor puede configurar el Iterador, se ordena en el orden de inserción o en el orden de acceso.

class LRUCache {
    
    

    int capacity;
    LinkedHashMap<Integer, Integer> cache;

    public LRUCache(int capacity) {
    
    
        this.capacity = capacity;
        cache = new LinkedHashMap<Integer, Integer>(capacity, 0.75f, true) {
    
    
            @Override
            protected boolean removeEldestEntry(Map.Entry eldest) {
    
    
                return cache.size() > capacity;
            }
        };
    }

    public int get(int key) {
    
    
        return cache.getOrDefault(key, -1);
    }

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

/**
 * Your LRUCache object will be instantiated and called as such:
 * LRUCache obj = new LRUCache(capacity);
 * int param_1 = obj.get(key);
 * obj.put(key,value);
 */

Cómo se ordena LinkedHashMap en el orden de acceso

El valor predeterminado es ordenar según el orden de inserción, en el método de construcción se puede configurar para ordenar según el orden de acceso

Inserte la descripción de la imagen aquí
Entonces, ¿qué significa ordenar por orden de visitas?

El método get (key) de LinkedHashMap se implementa por sí mismo y no se hereda de HashMap. Veamos cómo se ve la implementación del método get (Key).
Inserte la descripción de la imagen aquí
Veamos cómo se implementa el método afterNodeAccess ().

Inserte la descripción de la imagen aquí

	//本方法的作用:将结点移动到LinkedHashMap维护的双向链表的末尾
    void afterNodeAccess(Node<K,V> e) {
    
     // move node to last
        LinkedHashMap.Entry<K,V> last;
        if (accessOrder && (last = tail) != e) {
    
    
            LinkedHashMap.Entry<K,V> p =
                (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
            p.after = null;
            if (b == null)
                head = a;
            else
                b.after = a;
            if (a != null)
                a.before = b;
            else
                last = b;
            if (last == null)
                head = p;
            else {
    
    
                p.before = last;
                last.after = p;
            }
            tail = p;
            ++modCount;
        }
    }

Este método es principalmente para mover el puntero de la lista doblemente enlazada y mover el nodo entrante al final de la lista doblemente enlazada mantenida por LinkedHashMap, de modo que cada vez que se accede a un elemento a través del método get (clave), el elemento se moverá al final de la lista doblemente enlazada , Ordenados en el orden de acceso, es decir, cada vez que se atraviesa el keySet o EntrySet a través del Iterator, los elementos accedidos aparecerán al final (porque cuando el Iterator del LinedHashMap atraviesa, la lista interna doblemente enlazada se atraviesa desde el principio. Point, atraviesa hasta el nodo final)

Siguiendo esta línea de pensamiento, si se cumplen ciertas condiciones, se elimina el nodo principal de la lista doblemente enlazada, logrando así un LruCahe

De hecho, LinkedHashMap nos ha proporcionado un método de este tipo. Hay un método removeEldestEntry (entrada) en LinkedHashMap. Solo necesitamos anular este método y devolver verdadero bajo ciertas condiciones de acuerdo con nuestras propias necesidades, para que se realice la
implementación predeterminada del método LruCache . Devuelve falso
booleano protegido removeEldestEntry (Map.Entry <K, V> eldest) { return falso; }

El método afterNodeInsertion () de LinkedHashMap determinará si eliminar el elemento de encabezado de la lista doblemente vinculada en función de otras condiciones y el valor de retorno de removeEldestEntry

    void afterNodeInsertion(boolean evict) {
    
     // possibly remove eldest
        LinkedHashMap.Entry<K,V> first;
        if (evict && (first = head) != null && removeEldestEntry(first)) {
    
    
            K key = first.key;
            removeNode(hash(key), key, null, false, true);
        }
    }

referencia

El mecanismo de almacenamiento en caché de LRU (lru-cache) se implementa utilizando la versión de Java, lista doblemente vinculada + Mapa

Supongo que te gusta

Origin blog.csdn.net/e891377/article/details/108881241
Recomendado
Clasificación