LRU算法 + Java实现代码

本文详细介绍了LRU(最近最少使用)算法原理及其三种变体:LRU-K、URL-Two queues(2Q)和Multi Queue。LRU算法通过链表实现,新数据插入链表头部,淘汰尾部数据。LRU-K和2Q算法旨在解决LRU的缓存污染问题,提高命中率。Multi Queue根据访问频率划分多个优先级队列,进一步优化命中率。此外,文章还提供了基于HashMap和双向链表实现LRU缓存的Java代码示例。
摘要由CSDN通过智能技术生成

LRU原理

LRU(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。

  • 最常见的实现是使用一个链表保存缓存数据,详细算法实现如下

  1. 新数据插入到链表头部;
  2. 每当缓存命中(即缓存数据被访问),则将数据移到链表头部;
  3. 当链表满的时候,将链表尾部的数据丢弃。
    【命中率】
    当存在热点数据时,LRU的效率很好,但偶发性的、周期性的批量操作会导致LRU命中率急剧下降,缓存污染情况比较严重。
    【复杂度】
    实现简单。
    【代价】
    命中时需要遍历链表,找到命中的数据块索引,然后需要将数据移到头部。

LRU-K原理

LRU-K中的K代表最近使用的次数,因此LRU可以认为是LRU-1。LRU-K的主要目的是为了解决LRU算法“缓存污染”的问题,其核心思想是将“最近使用过1次”的判断标准扩展为“最近使用过K次”。

相比LRU,LRU-K需要多维护一个队列,用于记录所有缓存数据被访问的历史。只有当数据的访问次数达到K次的时候,才将数据放入缓存。当需要淘汰数据时,LRU-K会淘汰第K次访问时间距当前时间最大的数据。详细实现如下:

  1. 数据第一次被访问,加入到访问历史列表;
  2. 如果数据在访问历史列表里后没有达到K次访问,则按照一定规则(FIFO,LRU)淘汰;
  3. 当访问历史队列中的数据访问次数达到K次后,将数据索引从历史队列删除,将数据移到缓存队列中,并缓存此数据,缓存队列重新按照时间排序;
  4. 缓存数据队列中被再次访问后,重新排序;
  5. 需要淘汰数据时,淘汰缓存队列中排在末尾的数据,即:淘汰“倒数第K次访问离现在最久”的数据。
    LRU-K具有LRU的优点,同时能够避免LRU的缺点,实际应用中LRU-2是综合各种因素后最优的选择,LRU-3或者更大的K值命中率会高,但适应性差,需要大量的数据访问才能将历史访问记录清除掉。
    【命中率】
    LRU-K降低了“缓存污染”带来的问题,命中率比LRU要高。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: LRU算法(Least Recently Used)可以使用Java集合框架中的LinkedHashMap来实现,LinkedHashMap提供了一种按访问顺序排序的功能,具体实现示例如下:import java.util.*;public class LRUCache { // 容量 private int capacity; // 使用LinkedHashMap保存缓存的内容 private LinkedHashMap<Integer, Integer> cache; // 构造函数 public LRUCache(int capacity) { this.capacity = capacity; cache = new LinkedHashMap<Integer, Integer>(capacity, 0.75f, true){ // 重写removeEldestEntry方法 @Override protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) { return size() > capacity; } }; } // 根据key获取缓存内容 public int get(int key) { Integer value = cache.get(key); if (value == null) { return -1; } else { return value; } } // 存入key-value public void put(int key, int value) { cache.put(key, value); } } ### 回答2: LRU算法(Least Recently Used,最近最少使用)是一种用于缓存替换策略的算法,它保留最近使用的数据,并淘汰最久未使用的数据。以下是使用Java实现LRU算法的详细代码: ```java import java.util.HashMap; class Node { int key; int value; Node prev; Node next; public Node(int key, int value) { this.key = key; this.value = value; } } class LRUCache { private int capacity; private HashMap<Integer, Node> map; private Node head; private Node tail; public LRUCache(int capacity) { this.capacity = capacity; this.map = new HashMap<>(); this.head = new Node(0, 0); this.tail = new Node(0, 0); head.next = tail; tail.prev = head; } public int get(int key) { if (map.containsKey(key)) { Node node = map.get(key); remove(node); addToHead(node); return node.value; } return -1; } public void put(int key, int value) { if (map.containsKey(key)) { Node node = map.get(key); node.value = value; remove(node); addToHead(node); } else { if (map.size() == capacity) { map.remove(tail.prev.key); remove(tail.prev); } Node newNode = new Node(key, value); map.put(key, newNode); addToHead(newNode); } } private void addToHead(Node node) { node.next = head.next; node.next.prev = node; node.prev = head; head.next = node; } private void remove(Node node) { node.prev.next = node.next; node.next.prev = node.prev; } } ``` 本实现使用HashMap来存储缓存数据,HashMap的键为键值,值为对应的Node节点。 LRUCache类有一个构造函数用于初始化缓存容量。get方法用于获取缓存中的值,它首先判断缓存是否包含该键值,如果包含,则将对应的节点移至链表头部并返回对应的值;如果不包含则返回-1。put方法用于存储缓存数据,如果缓存中已存在该键值,则更新对应的值并将对应的节点移至链表头部;如果缓存大小已达到容量上限,则删除最久未使用的节点,并将新节点添加至链表头部。addToHead和remove方法用于调整链表节点的位置,将最近使用的节点移至链表头部,最久未使用的节点移至链表尾部。 以上就是使用Java实现LRU算法的详细代码。 ### 回答3: LRU(最近最少使用)算法是一种缓存淘汰策略,它根据缓存中数据的访问时间来进行淘汰。当缓存已满时,会淘汰掉最近最少使用的数据。下面是使用Java实现LRU算法的详细代码: ```java import java.util.HashMap; public class LRUCache { private int capacity; private HashMap<Integer, Node> cache; private Node head; private Node tail; public LRUCache(int capacity) { this.capacity = capacity; cache = new HashMap<>(); head = new Node(-1, -1); tail = new Node(-1, -1); head.next = tail; tail.prev = head; } public int get(int key) { if (cache.containsKey(key)) { Node node = cache.get(key); // 将访问的节点移到链表头部 removeNode(node); addToHead(node); return node.value; } return -1; } public void put(int key, int value) { if (cache.containsKey(key)) { Node node = cache.get(key); node.value = value; // 将访问的节点移到链表头部 removeNode(node); addToHead(node); } else { if (cache.size() == capacity) { // 移除最近最少使用的节点 cache.remove(tail.prev.key); removeNode(tail.prev); } Node newNode = new Node(key, value); cache.put(key, newNode); addToHead(newNode); } } private void removeNode(Node node) { node.prev.next = node.next; node.next.prev = node.prev; } private void addToHead(Node node) { node.next = head.next; node.prev = head; head.next.prev = node; head.next = node; } private class Node { private int key; private int value; private Node prev; private Node next; public Node(int key, int value) { this.key = key; this.value = value; } } } ``` 以上代码实现了一个LRUCache类,里面包含了LRU算法的核心逻辑。LRUCache类有一个构造函数用于初始化缓存容量,get()方法用于获取缓存中的数据,put()方法用于往缓存中添加数据。算法通过双向链表和哈希表实现,双向链表中的节点按照访问顺序从头到尾依次排列,哈希表用于加快查找节点的速度。当缓存已满时,淘汰最近最少使用的节点,即双向链表的尾节点。对于get操作和put操作,都会将访问的节点移到链表头部,保证头部节点是最近访问的节点。这样即可以实现LRU算法的缓存淘汰策略。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值