java LRU缓存实现

LinkedHashMap源码剖析

继承LinkedHashMap

可以通过继承 LinkedHashMap 实现 LRU 缓存结构。
代码如下:

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

    public LRUCache (int maxEntries){
//            /**
//             * Constructs an empty <tt>LinkedHashMap</tt> instance with the
//             * specified initial capacity, load factor and ordering mode.
//             *
//             * @param  initialCapacity the initial capacity
//             * @param  loadFactor      the load factor
//             * @param  accessOrder     the ordering mode - <tt>true</tt> for
//             *         access-order, <tt>false</tt> for insertion-order
//             * @throws IllegalArgumentException if the initial capacity is negative
//             *         or the load factor is nonpositive
//             */
//            public LinkedHashMap(int initialCapacity,
//            float loadFactor,
//            boolean accessOrder) {
//                super(initialCapacity, loadFactor);
//                this.accessOrder = accessOrder;
//            }
        super(maxEntries, 0.5f, true);
        MAX_ENTRIES = maxEntries;
    }

    protected boolean removeEldestEntry(Map.Entry eldest) {
         return size() > MAX_ENTRIES;
    }
}

自己的实现 (时间复杂度比较高,运行时间可能超时)

public class MyLRU<K, V> {

    private final int MAX_ENTRIES;
    private HashMap<K, V> map;
    private LinkedList<K> lruList;

    public MyLRU(int maxEntries){
        MAX_ENTRIES = maxEntries;
        map = new HashMap<>(maxEntries);
        lruList = new LinkedList<>();
    }

    public void set(K key, V value){
        map.put(key, value);
        afterAccess(key);
        afterInsert(key);
    }

    public V get(K key){
        V res = map.get(key);
        afterAccess(key);
        return res;
    }

    /**
     * 缓存溢出,去掉最近最少存取的元素,即链表头元素
     * @param key
     */
    private void afterInsert(K key) {
        // System.out.println("lruList.size: " + lruList.size());
        if(lruList.size() > MAX_ENTRIES) {
            K rmKey = lruList.removeFirst();
            // System.out.println("rmKey: " + rmKey);
            map.remove(rmKey);
        }
    }

    /**
     * 存取操作都将使对应的 key 移动到链表尾部
     * @param key
     */
    private void afterAccess(K key) {
        lruList.remove(key);
        lruList.addLast(key);
    }

}

改进的自定义实现(需要自定义结点和链表结构) (运行时间可以满足要求)

/**
 * @author 49367
 * @date 2021/3/29 15:23
 */
public class MyLRU<K, V> {

    private final int MAX_SIZE;
    private HashMap<K, LRUNode> cache;
    class LRUNode{
        K key;
        V val;
        LRUNode pre;
        LRUNode next;
        LRUNode(K key, V value){
            this.key = key;
            this.val = value;
        }
    }

    /**
     * 链表虚的头结点和尾结点,不存储任何数据
     * 当head.next = tail, tail.pre = head时,链表为空
     * 之所以设计成虚结点,是为了方便添加和删除操作
     */
    LRUNode head, tail;

    public MyLRU(int maxSize){
        MAX_SIZE = maxSize;
        cache = new HashMap<>(maxSize);
        head = new LRUNode(null, null);
        tail = new LRUNode(null, null);
        head.next = tail;
        head.pre = null;
        tail.next = null;
        tail.pre = head;
    }

    /**
     * 添加或者更新操作,如果是添加操作,需要进行缓存溢出判断,
     * 如果溢出,删去最近最少使用的数据。
     *
     * 将key对应的结点移动或者添加到表头
     * @param key
     * @param value
     */
    public void set(K key, V value){
        LRUNode node = cache.get(key);
        if (node == null) {
            // 如果 key 不存在,创建一个新的节点
            LRUNode newNode = new LRUNode(key, value);
            // 添加进哈希表
            cache.put(key, newNode);
            // 添加至双向链表的头部
            headAddNode(newNode);

            if (cache.size() > MAX_SIZE) {
                // 如果超出容量,删除双向链表的尾部节点
                LRUNode rmNode = removeTailNode();
                if(rmNode != null)
                    // 删除哈希表中对应的项
                    cache.remove(rmNode.key);
            }
        }
        else {
            // 如果 key 存在,先通过哈希表定位,再修改 value,并移到头部
            node.val = value;
            moveToHead(node);
        }
    }

    /**
     * 获取操作,将key对应的结点移动至链表头部
     * @param key
     * @return
     */
    public V get(K key){
        if(!cache.containsKey(key))
            return null;

        LRUNode node = cache.get(key);
        moveToHead(node);
        return node.val;
    }

    private void moveToHead(LRUNode node) {
        removeNode(node);
        headAddNode(node);
    }

    private void removeNode(LRUNode node){
        if(node != null) {
            node.pre.next = node.next;
            node.next.pre = node.pre;
            node.pre = null;
            node.next = null;
        }
    }

    private LRUNode removeTailNode(){
        if(tail.pre == head)
            return null;

        LRUNode rmNode = tail.pre;
        removeNode(rmNode);
        return rmNode;
    }

    private void headAddNode(LRUNode node){
        LRUNode next = head.next;
        head.next = node;
        node.pre = head;
        node.next = next;
        next.pre = node;
    }
  
}

相关题目

牛客NC93设计LRU缓存结构
leetcode146 LRU 缓存机制

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值