ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

LRU缓存机制

2021-07-19 21:29:33  阅读:203  来源: 互联网

标签:node map 结点 缓存 int DLinkedNode LRU key 机制


题目链接

LRU缓存机制

题目描述

注意点

  • 当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间

解答思路

  • LRU缓存机制的相关解释
  • 需要根据键来取得值,所以考虑用map来存储数据
  • 使用一个双链表来存储数据值的位置,方便后续删除结点

代码

class LRUCache {
    class DLinkedNode {
        int key;
        int value;
        DLinkedNode prev;
        DLinkedNode next;
        public DLinkedNode() {}
        public DLinkedNode(int key, int value) {this.key = key; this.value = value;}
    }

    //用于存储键值对
    Map<Integer,DLinkedNode> map = new HashMap<>();
    //空间大小
    int capacity = 0;
    int size = 0;
    //虚拟头结点和尾结点,方便后续删除和添加结点
    DLinkedNode head;
    DLinkedNode tail;

    //初始化
    public LRUCache(int capacity) {
        this.capacity = capacity;
        head = new DLinkedNode();
        tail = new DLinkedNode();
        head.next = tail;
        tail.prev = head;
    }
    
    public int get(int key) {
        if(map.get(key) == null){
            return -1;
        }else{
            if(map.get(key) != head.next){
                //能找到该结点,则需要将该结点放到头部
                moveToHead(map.get(key));
            }
            return map.get(key).value;
        }
    }
    
    public void put(int key, int value) {
        //将该结点添加进DLinkedNode中
        //map中是否已经有该key值
        if(map.get(key) != null){
            DLinkedNode node = map.get(key);
            node.value = value;
            moveToHead(node);
        }else{
            //判断是否超出容量
            if(size == capacity){
                //超出容量的情况
                //先删除最后一个结点
                delTail();
                DLinkedNode node = new DLinkedNode(key,value);
                map.put(key,node);
                //将该结点设置为头结点
                setHead(node);
            }else{
                //没有超出容量,直接将该结点添加进map中,并设置为头结点
                DLinkedNode node = new DLinkedNode(key,value);
                map.put(key,node);
                setHead(node);
                size++;
            }
        }
        
    }

    //将双链表中的某个结点设置为头结点
    public void moveToHead(DLinkedNode node){
        node.prev.next = node.next;
        node.next.prev = node.prev;
        setHead(node);
    }

    //设置头结点
    public void setHead(DLinkedNode node){
        DLinkedNode nex = head.next;
        head.next = node;
        node.prev = head;
        nex.prev = node;
        node.next = nex;
    }

    //删除尾结点
    public void delTail(){
        //从map中删除
        map.remove(tail.prev.key);
        tail.prev = tail.prev.prev;
        tail.prev.next = tail;
    }
}

/**
 * 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);
 */

/**
 * 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);
 */

关键点

  • 在创建链表时创建了虚拟的头结点和尾结点,所以下面所说的头结点和尾结点都是指虚拟头结点的下一个结点和虚拟尾结点的上一个结点,可以使双向链表中的每一个结点的移动方式都相同而不需要判断是否有前结点和后结点,大大降低空指针异常发生的概率
  • 利用map存储数据,可以通过key值获取value值
  • 通过双链表存储数据,可以存储各个结点的位置
  • 每次对数据进行操作后,需要将该数据移动至头结点
  • 每次添加前,需要先判断该数据的键在map中是否已经存在,如果存在则只需要改变原双链表中键所对应的值即可,同时也要将该数据移动至头结点
  • 添加时,如果容量已满,同时在map中找不到新数据的键,则需要先删除尾结点的数据,删除尾结点数据的同时还要将尾结点的数据从map中移除,然后再将新的数据添加至头结点中
  • 添加至头结点和移动至头结点操作并不完全相同,区别在于移动至头结点还多了一步,即改变该结点的前后结点的索引

标签:node,map,结点,缓存,int,DLinkedNode,LRU,key,机制
来源: https://blog.csdn.net/weixin_51628158/article/details/118888751

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有