java缓存淘汰策略_双链表实现LRU缓存淘汰策略

1.背景

LRU(Least Recently Used)是一种常用的缓存淘汰策略,即当缓存满了之后,删除最近最少使用的数据。

LRU的实现,常用的编程语言在语言层面都有实现,可以直接使用。为了深入理解LRU,本文介绍一种通过双链表实现LRU方法。

2.双链表实现方法

Java版双链表实现代码。

2.1 定义节点实体类

/**

* 链表节点.

* @author liunan1 .

*/

public class Node {

private int key;

private int value;

private Node next;

private Node prev;

public Node(int key, int value) {

this.key = key;

this.value = value;

}

public int getKey() {

return key;

}

public int getValue() {

return value;

}

public Node getNext() {

return next;

}

public void setNext(Node next) {

this.next = next;

}

public Node getPrev() {

return prev;

}

public void setPrev(Node prev) {

this.prev = prev;

}

}

2.2 双链实现类

package lewis.test.algorithm.lru.model;

import java.util.Objects;

/**

* 双链表.

* @author liunan1 .

*/

public class IntList {

private int length;

private Node head;

private Node tail;

/**

* 删除最后一个节点.

*/

public void removeLast() {

if (Objects.nonNull(tail) && Objects.nonNull(tail.getPrev())) {

Node prev = tail.getPrev();

prev.setNext(null);

tail = prev;

--length;

}

}

/**

* 查找数据,未找到返回-1.

* @param key

* @return

*/

public int get(int key) {

int i = 1;

Node currentNode = head;

for(; i <= length; i++) {

int findKey = currentNode.getKey();

int value = currentNode.getValue();

Node prev = currentNode.getPrev();

Node next = currentNode.getNext();

if (findKey == key) {

// 将节点移动到链表头部

if (Objects.nonNull(prev)) {

// 1.将节点移除

prev.setNext(next);

if (Objects.nonNull(next)) {

next.setPrev(prev);

} else {

// 节点队尾,更新队尾指针.

tail = prev;

}

// 2. 将节点移动到表头

currentNode.setNext(head);

head.setPrev(currentNode);

currentNode.setPrev(null);

head = currentNode;

}

return value;

}

currentNode = next;

}

// 未找到

return -1;

}

/**

* 头部插入节点.

* @param node node.

*/

public void addHead(Node node) {

head.setPrev(node);

node.setNext(head);

head = node;

++length;

}

/**

* 链表长度.

* @return 长度.

*/

public int size() {

return length;

}

}

2.3 LRU模拟实现类

package lewis.test.algorithm.lru.model;

import java.util.Random;

/**

* LRU缓存淘汰策略实现类.

* @author liunan1 .

*/

public class LeastRecentlyUsedCache {

// 缓存容量

private int capacity;

private IntList list;

public LeastRecentlyUsedCache(int capacity) {

this.capacity = capacity;

}

/**

* 从缓存中获取数据.

* @param key .

* @return

*/

public int get(int key) {

int value = this.list.get(key);

// 未找到,从磁盘读取.

if (-1 == value) {

// 模拟从磁盘读取.

value = new Random().nextInt(100);

// 插入缓存.

put(key, value);

}

return value;

}

/**

* 插入数据.

* @param key key.

* @param value value.

*/

public void put(int key, int value) {

Node node = new Node(key, value);

list.addHead(node);

if (list.size() > capacity) {

list.removeLast();

}

}

}

3. 总结与思考

通过三个Java类,模拟实现了LRU,简单明了,易于理解。用心的读取也许已经发现,该实现查找缓存的时候,性能不够好,时间复杂度是O(n)。后续将对该实现进行优化,将时间复杂度提升到O(1)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 可以使用java中的LinkedHashMap类来实现LRU缓存淘汰策略。LinkedHashMap是一种特殊的HashMap,它不仅保存了键值对,还维护了一个双向链表来记录插入顺序,当缓存满后,它会将最先插入的元素淘汰掉。 ### 回答2: LRU(最近最少使用)缓存淘汰策略是指在缓存满时,优先淘汰最近最少使用的数据。实现LRU缓存淘汰策略可以使用链表和哈希表的组合。 首先,我们可以创建一个双向链表的节点类,包含键值对的信息,以及前后指针。链表的头部表示最近访问的节点,尾部表示最久未使用的节点。 我们还需要一个哈希表,用于快速查找节点。哈希表的键是缓存的键,值是对应的节点。 接下来,我们需要实现缓存淘汰策略的几个操作: 1. 获取操作:当尝试获取缓存中的键值对时,如果哈希表中存在该键,则将对应的节点移动到链表头部,并返回该值。如果哈希表中不存在该键,则返回空。 2. 添加操作:当添加新的键值对时,如果该键已存在于缓存中,则将该节点移到链表头部,更新值。否则,创建新节点,并将其插入到链表头部和哈希表中。如果缓存已满,删除链表尾部的节点,并在哈希表中删除对应的键。 3. 删除操作:当从缓存中删除某个键值对时,找到对应的节点,从链表中删除该节点,并在哈希表中删除该键。 以上就是使用Java链表实现LRU缓存淘汰策略的基本思路。通过使用双向链表和哈希表的组合,能够快速实现缓存的增删改查操作,并保持对节点访问的顺序,以便实现LRU策略。 ### 回答3: LRU(Least Recently Used,最近最少使用)是一种常用的缓存淘汰策略,当缓存满时,会将最长时间未被使用的数据从缓存中淘汰。实现LRU缓存淘汰策略可以使用Java中的链表来实现Java中已经提供了LinkedList类,可以直接用于实现链表数据结构。接下来是一个简单的实现LRU缓存的示例代码: ``` import java.util.LinkedList; public class LRUCache<K, V> { private int capacity; private LinkedList<Entry<K, V>> cache; public LRUCache(int capacity) { this.capacity = capacity; this.cache = new LinkedList<>(); } public V get(K key) { Entry<K, V> entry = findEntryByKey(key); if (entry != null) { // 将使用的数据移动到链表头部 cache.remove(entry); cache.addFirst(entry); return entry.value; } return null; } public void put(K key, V value) { Entry<K, V> entry = findEntryByKey(key); if (entry != null) { // 如果key已存在,更新其value,并将数据移动到链表头部 entry.value = value; cache.remove(entry); cache.addFirst(entry); } else { // 如果key不存在,先判断容量是否已满,如果已满则移除最久未使用的数据 if (cache.size() >= capacity) { cache.removeLast(); } // 将新数据插入到链表头部 Entry<K, V> newEntry = new Entry<>(key, value); cache.addFirst(newEntry); } } private Entry<K, V> findEntryByKey(K key) { for (Entry<K, V> entry : cache) { if (entry.key.equals(key)) { return entry; } } return null; } private static class Entry<K, V> { private K key; private V value; public Entry(K key, V value) { this.key = key; this.value = value; } } } ``` 使用示例: ``` LRUCache<String, Integer> cache = new LRUCache<>(3); cache.put("a", 1); cache.put("b", 2); cache.put("c", 3); System.out.println(cache.get("a")); // 输出1 cache.put("d", 4); System.out.println(cache.get("b")); // 输出null,因为b是最久未使用的数据,已被淘汰 ``` 在LRUCache类中,使用LinkedList作为缓存的数据存储结构。缓存的最近使用的数据总是位于链表的头部,当需要访问或更新数据时,将其移动到链表头部。当缓存已满时,移除链表尾部的最久未使用的数据。 这种实现方式可以在O(1)的时间复杂度内实现get和put操作,符合LRU缓存淘汰策略的特性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值