前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >设计LRU 缓存结构

设计LRU 缓存结构

作者头像
RAIN7
发布2023-04-04 10:42:09
3110
发布2023-04-04 10:42:09
举报
文章被收录于专栏:RAIN7 de 编程之路

设计LRU 缓存结构

代码语言:javascript
复制
import java.util.*;

/**
 *  LRU 最近最少使用,内存淘汰算法
 */
public class LRUCache {

    // 双向链表, 访问节点,说明使用一次,那么这个节点就移动到头部作为热门数据,其余数据位置不变,尾部就是最近最少使用的节点
    // 怎么插入呢? 如果缓存为空,那么头插法插入。如果缓存满了,先将尾节点删除,然后插入到头节点后面

    // 双向链表删除尾节点、插入头节点的效率都是O(1)
    // 但是如果我们去访问节点的话,双向链表还是遍历O(N)的复杂度,
    // 所以就可以通过HashMap 记录key(访问值),value(链表的节点),达到O(1)的效率

    Entry head,tail;// 代表双向链表的头节点和尾节点
    int capacity;// 缓存的大小
    int size;// 实际存储的大小
    Map<Integer,Entry> cache; // 查缓存的哈希表

    public LRUCache(int capacity) {
        // 实际上双向链表中是真正的LRU缓存数据
        initLinkedList();
        this.capacity =capacity;
        this.size=0;
        this.cache = new HashMap<>(capacity);
    }

    // 向缓存中放一个值

    /**
     *
     * @param key , 就是我们要放入的元素 1、2、3、4、5、6
     * @param value , 就是这个元素 对应的在双向链表中的 一个节点
     */
    public void put(int key,int value){
        // 先查这个key在双向链表中是否存在
        Entry node =cache.get(key);
        if(node!=null){// 说明这个key已经在 linkedList中存在,那么首先更新value
            node.value = value;
            // 删掉这个节点的连接关系,然后移动到头节点
            moveToHead(node);
            return;
        }


        // 删 、 添 都需要 操作(链表+缓存Map)
        if(size==capacity){// 此时内存已经满了,需要将尾节点的元素删除,然后构建新节点插入到头节点后面
            Entry lastNode = tail.pre;
            // 删除链表中的一个元素
            deleteNode(lastNode);
            // 删除缓存中的一个节点
            cache.remove(lastNode.key);
            size--;
        }

        // 此时是内存不满,而且key在链表中不存在的情况,头插法查到头节点后面即可
        Entry newHead = new Entry(key,value);
        addToHead(newHead);
        // 也要添加到缓存中
        cache.put(key, newHead);
        size++;
    }

    public int get(int key){
        Entry node = cache.get(key);
        if(node==null){
            return -1;
        }
        // 因为查询了一次,所以这个node又变成了热点数据,需要先删节点关系,然后移动到头节点后面
        int result = node.value;
        moveToHead(node);
        return result;
    }

    /**
     *  删除最后的尾节点前面的节点即可
     * @param node
     */
    private void deleteNode(Entry node) {
           node.pre.next = tail;
           tail.pre = node.pre;
    }

    private void addToHead(Entry node) {
        node.next = head.next;
        head.next.pre = node;

        head.next = node;
        node.pre = head;
    }

    public void moveToHead(Entry node){
        deleteNode(node);
        addToHead(node);
    }



    // 初始化链表的方法
    private void initLinkedList() {
       this.head = new Entry(0,0);
       this.tail = new Entry(0,0);

       head.next = tail;
       tail.pre = head;
    }

    // 代表双向链表的一个节点
    public static class Entry{
        public Entry pre;
        public Entry next;
        public int key;
        public int value;

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


}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023/04/03 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 设计LRU 缓存结构
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档