🔥皮皮虾今天就教你如何用链表来实现 LRU 缓存淘汰策略?

976 阅读4分钟

本文已参与掘金创作者训练营第三期「话题写作」赛道,详情查看:掘力计划|创作者训练营第三期正在进行,「写」出个人影响力

Code皮皮虾 一个沙雕而又有趣的憨憨少年,和大多数小伙伴们一样喜欢听歌、游戏,当然除此之外还有写作的兴趣,emm...,日子还很长,让我们一起加油努力叭🌈

欢迎各位小伙伴们关注我的公众号:JavaCodes,名称虽带Java但涉及范围可不止Java领域噢😁,会长期分享博文或者福利,期待您的关注❤


😉毛遂自荐

毛遂自荐,给大家推荐一下自己的专栏😁,欢迎小伙伴们收藏关注😊

小白学Java

MybatisPlus专栏

App爬虫专栏

PC端爬虫专栏

大厂面试题专栏


✨LRU介绍

根据百度百科的介绍:LRU_百度百科 (baidu.com)

LRU是Least Recently Used的缩写,即最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰。该算法赋予每个页面一个访问字段,用来记录一个页面自上次被访问以来所经历的时间 t,当须淘汰一个页面时,选择现有页面中其 t 值最大的,即最近最少使用的页面予以淘汰。

那么按照我的话来说就是:淘汰最近最少使用到的数据


☀LRU使用场景

  1. 操作系统的LRU页面置换算法
  2. Mysql缓冲池使用的LRU算法对数据页进行管理
  3. Redis的缓存淘汰策略也有volatile-lru 和 allkeys-lru 都是使用到的LRU算法

🔥实现LRU算法

🔥实现LRU——LRU相关练习题

力扣上的LRU算法题链接

可见该题的点赞数和评论、题解量都是非常之高

image.png

牛客上该题链接

image.png



🔥实现LRU——前提概要讲解✨

题目已经说过:如何用链表来实现 LRU 缓存淘汰策略?

那么对于实现LRU所使用的链表不是普通链表而是双向链表

而且在面试过程中,面试官不会让你使用已有的库,例如:LinkedList之类底层使用链表的集合类来实现LRU算法

所以对于双向链表我们需要手动实现

class DoubleLinkedList {
    int key;
    int value;
    DoubleLinkedList prev;
    DoubleLinkedList next;

    public DoubleLinkedList() {
    }
    public DoubleLinkedList(int key, int value) {
        this.key = key;
        this.value = value;
    }
}

除此之外,我们还定义虚拟头节点、尾节点来规定区间

🔥而且,从头结点到尾节点使用次数逐渐减少,也就是说从最近最多使用逐渐到最近最少使用!!!

private DoubleLinkedList head,tail;

光有双向链表可还不够,因为LRU是淘汰最近最少使用,那么还需要记录最少使用

那么,我们使用HashMap来记录使用的次数

private Map<Integer,DoubleLinkedList> map;

☀前提概要大概就这些了,那么接下来我们进入完整代码流程部分



🔥实现LRU——完整代码注释讲解✨

import java.util.HashMap;
import java.util.Map;


public class LruDemo {

    //定义双向链表
    class DoubleLinkedList {
        int key;
        int value;
        DoubleLinkedList prev;
        DoubleLinkedList next;

        public DoubleLinkedList() {
        }
        public DoubleLinkedList(int key, int value) {
            this.key = key;
            this.value = value;
        }
    }
   
    //当前容量
    private int size;
    //最大容量
    private int capacity;
    //记录使用次数
    private Map<Integer,DoubleLinkedList> map;
    //虚拟头节点、尾节点
    private DoubleLinkedList head,tail;

    //构造函数进行初始化
    public LruDemo(int capacity) {
        this.size = 0;
        this.capacity = capacity;
        this.map = new HashMap<>();
         // 使用伪头部和伪尾部节点
        this.head = new DoubleLinkedList();
        this.tail = new DoubleLinkedList();

        head.next = tail;
        tail.prev = head;
    }


    //LRU——get方法
    public int get(int key) {
        //如果不存在key
        if (!map.containsKey(key)) {
            return -1;
        }

        DoubleLinkedList node = map.get(key);
        
        // 如果 key 存在,先通过哈希表定位,再移到头部(因为他是最近使用到的,所以要放到头节点)
        moveToHead(node);
        return node.value;
    }


    //LRU——put方法
    public void put(int key,int value) {

        DoubleLinkedList node = map.get(key);
	//如果不存在
        if (node == null) {

            DoubleLinkedList newNode = new DoubleLinkedList(key, value);
            //移动到头节点
            addHead(newNode);
            map.put(key,newNode);
            size++;
		   
            //如果容量满了
            if (size > capacity) {
                //删除末尾节点接就是最久未使用的
                DoubleLinkedList lastNode = removeLastNode();
                map.remove(lastNode.key);
                size--;
            }

        }else {
            //更新value值
            node.value = value;
            moveToHead(node);
        }

    }

    //删除末尾节点
    private DoubleLinkedList removeLastNode() {
        DoubleLinkedList last = tail.prev;

        removeNode(last);
        return last;
    }

    //移动节点到头节点
    private void moveToHead(DoubleLinkedList node) {
        removeNode(node);
        addHead(node);
    }

    //删除节点
    private void removeNode(DoubleLinkedList node) {
        node.next.prev = node.prev;
        node.prev.next = node.next;
        node.next = null;
        node.prev = null;
    }

    //添加节点(默认都是添加到头节点)
    private void addHead(DoubleLinkedList node) {
        node.next = head.next;
        node.prev = head;
        head.next.prev = node;
        head.next = node;
    }

}

❤最后

我是 Code皮皮虾,一个热爱分享知识的 皮皮虾爱好者,未来的日子里会不断更新出对大家有益的博文,期待大家的关注!!!

创作不易,如果这篇博文对各位有帮助,希望各位小伙伴可以一键三连哦!,感谢支持,我们下次再见~~~,不,我们天天见😊

欢迎各位小伙伴们关注我的公众号:JavaCodes,名称虽带Java但涉及范围可不止Java领域噢😁,会长期分享博文或者福利,期待您的关注❤



一键三连.png