文章目录
1、内存空间有限,当缓存满的时候,如何淘汰缓存?
2、实现LRU demo
- 使用Java容器LinkedHashMap
- 哈希表(HashMap)+双向链表
1、内存空间有限,当缓存满的时候,如何淘汰缓存?
FIFO(First In First Out)先进先出
LFU(Least Frequently Used)最不经常使用
LRU(Least Recently Used)最近最少使用
2、实现LRU demo
1、使用Java容器LinkedHashMap
LinkedHashMap本身就具有LRU算法的特性
class LRUCache { private Map<Integer, Integer> cacheMap = null; public LRUCache(int capacity) { // 参数设置true,当removeEldestEntry()返回true,则删除最旧的数据 cacheMap = new LinkedHashMap<Integer, Integer>(capacity,0.75F,true){ @Override protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) { return size() > capacity; } }; } public int get(int key) { return cacheMap.getOrDefault(key, -1); } public void put(int key, int value) { cacheMap.put(key, value); } }
2、哈希表(HashMap)+双向链表
维护一个双向链表,靠近链表尾部的结点是越早访问的,靠近头部的节点是最近访问的。
如果此数据之前已经被缓存在链表中了,我们遍历得到这个数据对应的结点,并将其从原来的位置删除,然后再插入到链表的头部。
如果此数据没有在缓存链表中,又可以分为两种情况:
如果此时缓存未满,则将此结点直接插入到链表的头部
如果此时缓存已满,则链表尾结点删除,将新的数据结点插入链表的头部。
class LRUCache2 { // 当前缓存容量 private int size; // 限制最大缓存容量 private int capacity; // 定义伪头尾节点 private Node head; private Node tail; // 定义HashMap存储数据 private Map<Integer, Node> cache = new HashMap(); // 初始化操作 public LRUCache2(int capacity) { size = 0; this.capacity = capacity; // 初始化头尾节点 head = new Node(); tail = new Node(); // 让头尾节点相联 head.next = tail; tail.pre = head; } public int get(int key) { Node node = cache.get(key); // 不存在返回-1 if (null == node) { return -1; } // 存在返回值,并且将当前节点移动到头 moveNodeHead(node); return node.value; } public void put(int key, int value) { Node node = cache.get(key); // 不存在则插入,插入后判断当前容量是否大于限制最大容量 if (null == node) { Node newNode = new Node(key, value); cache.put(key, newNode); // 放入链表头部 addNodeHead(newNode); size++; if (size > capacity) { // 删除尾结点 Node tail = removeNodeTail(); cache.remove(tail.key); } } else { // 存在则覆盖value,并且将当前节点移动到头 node.value = value; moveNodeHead(node); } } // 放入链表的头部 private void addNodeHead(Node node) { // 当前头节点的下一个节点的pre和当前节点连接 head.next.pre = node; // node.next = head.next; // head.next = node; node.pre = head; } // 将节点移动到链表的头部 private void moveNodeHead(Node node) { node.pre.next = node.next; node.next.pre = node.pre; addNodeHead(node); } // 删除链表的尾结点 private Node removeNodeTail() { Node tailNode = tail.pre; tailNode.pre.next = tailNode.next; tailNode.next.pre = tailNode.pre; return tailNode; } // 定义一个双向链表,实际的缓存 class Node { private int key; private int value; private Node pre; private Node next; public Node() { } public Node(int key, int value) { this.key = key; this.value = value; } } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
2018-12-12 windows中git输错密码后不能重新输入的问题