最近在看redis的缓存淘汰策略,其中的LRU算法用JAVA实现了一下,没有用LinkedHashMap,代码如下
public class LRU {
// 缓存最大长度
private Integer length;
// 存放缓存节点的map,用于取值
private Map<String, DoubleLinkList.Node> map;
// 缓存双向链表
private DoubleLinkList doubleLinkList;
/**
* 初始化
*/
public LRU(Integer length) {
this.length = length;
this.map = new HashMap<>();
this.doubleLinkList = new DoubleLinkList();
}
/**
* 获取缓存节点
*/
public String get(String key) {
// 获取node节点
DoubleLinkList.Node keyNode = map.get(key);
if (keyNode == null) {
System.out.println("节点"+ key + "不存在");
return null;
}
System.out.println("节点"+ key + "存在, 移到队列头部");
// 将节点从原来位置删除,加到头部
doubleLinkList.del(keyNode);
doubleLinkList.addHead(keyNode);
return keyNode.getValue();
}
/**
* 添加缓存节点
*/
public void put(String key, String value) {
DoubleLinkList.Node existNode = map.get(key);
// key存在, 则更新key的值,并将节点放入头部
if (existNode != null) {
System.out.println("节点" + key + "已存在,移到队列头部");
existNode.setValue(value);
doubleLinkList.del(existNode);
doubleLinkList.addHead(existNode);
} else { // key不存在,则新增节点
DoubleLinkList.Node newNode = new DoubleLinkList.Node(key, value);
System.out.println("节点" + key + "不存在,新增节点");
if (map.size() == length) {
// 节点长度已经到达最大长度时,需要删除末尾节点,并将节点加入头部
DoubleLinkList.Node lastNode = doubleLinkList.getLast();
System.out.println("缓存队列已达到最大长度,删除末尾节点" + lastNode.getKey());
doubleLinkList.del(lastNode);
doubleLinkList.addHead(newNode);
map.remove(lastNode.getKey());
} else {
doubleLinkList.addHead(newNode);
map.put(key, newNode);
}
}
}
/**
* 双向链表实现
*/
private static class DoubleLinkList {
// 头部指针,不存值
private Node head;
// 尾部指针,不存值
private Node tail;
/**
* 初始化,头尾相连
**/
public DoubleLinkList() {
head = new Node();
tail = new Node();
head.setPre(tail);
head.setNext(tail);
tail.setNext(head);
tail.setPre(head);
}
/**
* 添加到双向链表头部
*/
public void addHead(Node node) {
Node oldHeadNext = head.getNext();
oldHeadNext.setPre(node);
node.setNext(oldHeadNext);
node.setPre(head);
head.setNext(node);
}
/**
* 删除节点
*/
public void del(Node node) {
Node pre = node.getPre();
Node next = node.getNext();
pre.setNext(next);
next.setPre(pre);
}
/**
* 获取链表末尾节点
*/
public Node getLast() {
return tail.getPre();
}
/**
* 双向链表的节点类
*/
private static class Node {
private String key;
private String value;
private Node pre;
private Node next;
public Node() {
pre = null;
next = null;
}
public Node(String key, String value) {
this.key = key;
this.value = value;
pre = null;
next = null;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public Node getPre() {
return pre;
}
public void setPre(Node pre) {
this.pre = pre;
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
}
@Override
public String toString() {
String a = "";
Node node = head.next;
while (node != null && node != tail) {
a = a + "【节点:" + node.getKey() +" 值:" + node.getValue() + "】";
node = node.next;
}
return a;
}
}
@Override
public String toString() {
return "LRU{" +
"缓存长度=" + length +
", 缓存队列=" + doubleLinkList.toString() +
'}';
}
public static void main(String[] args) {
System.out.println("初始化……");
LRU lru = new LRU(3);
System.out.println(lru);
System.out.println("加入节点 1 ……");
lru.put("1","aaa");
System.out.println(lru);
System.out.println("加入节点 2 ……");
lru.put("2","bbb");
System.out.println(lru);
System.out.println("加入节点 3 ……");
lru.put("3","ccc");
System.out.println(lru);
System.out.println("加入节点 4 ……");
lru.put("4","ddd");
System.out.println(lru);
System.out.println("加入节点 2 ……");
lru.put("2","bbbb");
System.out.println(lru);
System.out.println("获取节点 3 ……");
System.out.println(lru.get("3"));
System.out.println(lru);
}
}
运行输出如下:
初始化……
LRU{缓存长度=3, 缓存队列=}
加入节点 1 ……
节点1不存在,新增节点
LRU{缓存长度=3, 缓存队列=【节点:1 值:aaa】}
加入节点 2 ……
节点2不存在,新增节点
LRU{缓存长度=3, 缓存队列=【节点:2 值:bbb】【节点:1 值:aaa】}
加入节点 3 ……
节点3不存在,新增节点
LRU{缓存长度=3, 缓存队列=【节点:3 值:ccc】【节点:2 值:bbb】【节点:1 值:aaa】}
加入节点 4 ……
节点4不存在,新增节点
缓存队列已达到最大长度,删除末尾节点1
LRU{缓存长度=3, 缓存队列=【节点:4 值:ddd】【节点:3 值:ccc】【节点:2 值:bbb】}
加入节点 2 ……
节点2已存在,移到队列头部
LRU{缓存长度=3, 缓存队列=【节点:2 值:bbbb】【节点:4 值:ddd】【节点:3 值:ccc】}
获取节点 3 ……
节点3存在, 移到队列头部
ccc
LRU{缓存长度=3, 缓存队列=【节点:3 值:ccc】【节点:2 值:bbbb】【节点:4 值:ddd】}