【LRU】LinkedHashMap 实现 LRUCache

183 阅读2分钟

1 结构示意图

2 源码实现

package classloader.lc;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * LinkedHashMap实现Map的接口,和HashMap不同的是维持了一个所有entries的双向链表,
 * 并持有一个该有序链表的迭代器,并有两个Entry<K,V>引用transient LinkedHashMap.Entry<K,V> head,tail
 * 分别指向链表的首部和尾部,最常使用的元素会放到链表的尾部,链表的头部为最不常用的数据,
 * 同时插入一个新元素也会插入到尾部,我们可以用这个特性来实现LRU(最近最不常使用)缓存;
 * The tail (youngest) of the doubly linked list.
 * The head (eldest) of the doubly linked list.
 *
 * 查询元素 get-->accessOrder == true --> afterNodeAccess  move node to last 实现最近访问移动到尾部;
 *
 * boolean removeEldestEntry(Map.Entry)该方法返回值为true时,会删除最近最不常使用的元素,
 * 也就是double-link的头部节点,当插入一个新节点之后removeEldestEntry()方法会被put()、putAll()方法调用,
 * 我们可以通过override该方法,来控制删除最旧节点的条件
 * 即 put--> afterNodeInsertion---->removeEldestEntry  需要自定义实现保留元素的长度
 *
 */
class LRUCache extends LinkedHashMap<Integer, Integer> {
    private int maxSize;
    public LRUCache(int capacity) {
        // 第三个参数表示开启lru
        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);
    }

    // Here is my implementation by using LinkedHashMap in AccessOrder.
    // It will move the latest accessed element to the front which only
    // incurs O(1) overhead because the underlying elements are
    // organized in a doubly-linked list while also are indexed by hash function.
    // So the get/put/top_newest_one operations all cost O(1).
    // 当前size()大于了maxSize便删掉头部的元素, 先从map中移除,再从双向链表中移除
    @Override
    protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {
        return this.size() > maxSize; //must override it if used in a fixed cache
    }
}

3 小结

  • 1 整体结构 hashmap + 双向链表
  • 2 LRU缓存