这个双向链表采用的是有伪头节点和伪尾节点的 与上一篇文章中单链表的实现不同,区别于在实例化这个链表时就初始化了的伪头节点和伪尾节点,并相互指向,在第一次添加节点时,不需要再考虑空指针指向问题了。
java
/**
* 通过链表与HashMap实现LRU缓存
*
* @author CC
* @version 1.0
* @since2023/9/27
*/
public class LRUCache {
private Map<Integer, Node> cache = new HashMap<>();//哈希表
private int size;//链表长度
private int capacity;//缓存容量
private Node first;//伪头节点
private Node last;//伪尾节点
/**
* 将一个新节点添加到头部
*
* @param newNode 要添加的新节点
*/
private void addFirst(Node newNode) {
//注意: 顺序很重要
//1、分配新节点的前驱和后继
newNode.prev = first;
newNode.next = first.next;
//2、头节点原来的后继的前驱指向新节点
first.next.prev = newNode;
//3、头节点的后继执行新节点
first.next = newNode;
}
/**
* 删除一个节点
*
* @param node 要删除的节点
*/
private void deleteNode(Node node) {
//要删除节点的后继和前驱相互指引
node.prev.next = node.next;
node.next.prev = node.prev;
}
/**
* 将一个节点放到伪头节点后
*
* @param node 移动的节点
*/
private void moveToFirst(Node node) {
//删除这个节点
deleteNode(node);
//添加一个头节点
addFirst(node);
}
/**
* 删除尾节点
*
* @return 返回删除的这个节点
*/
private Node deleteToLast() {
//获得伪尾节点的前驱 也就是尾节点
Node ret = last.prev;
//删除尾节点
deleteNode(last.prev);
return ret;
}
/**
* 存入缓存
*
* @param key
* @param value
*/
public void put(int key, int value) {
//从hash表中查询这个健
Node node = cache.get(key);
//如果hash表中不存在要添加的健
if (node == null) {
//创建一个新的节点
Node newNode = new Node(key, value);
//将这个健和节点添加到hash表中
cache.put(key, newNode);
//将这个节点存到头节点中
addFirst(newNode);
//如果这个缓存已满
if (++size > capacity) {
//删除尾节点
Node last = deleteToLast();
//从hash表中也删除这个健
cache.remove(last.key);
size--;
}
//如果hash表中存在要添加的健
} else {
//将新添加的值覆盖原来的值
node.value = value;
//并移到头节点
moveToFirst(node);
}
}
/**
* 获取缓存
*
* @param key 该缓存的健
* @return 返回 该节点的值
*/
public int get(int key) {
//通过健从hash表中获取这个节点
Node node = cache.get(key);
//如果为空 则返回-1
if (node == null) {
return -1;
}
//否则 将该节点 移到头节点处
moveToFirst(node);
return node.value;
}
/**
* 双向链表的遍历 头->尾
*
* @return
*/
@Override
public String toString() {
StringJoiner sj = new StringJoiner("->");
for (Node n =first.next;n.next!=null;n=n.next){
sj.add(String.valueOf(n.value));
}
return "头->尾:"+sj.toString();
}
/**
* 构造方法
*
* @param capacity 设置缓存容量
*/
public LRUCache(int capacity) {
size = 0;//初始链表长度位0
this.capacity = capacity;//设置缓存容量
first = new Node();//实例化伪头节点
last = new Node();//实例化伪尾节点
//初始头尾节点相互指向
first.next = last;
last.prev = first;
}
/**
* 节点类
*/
class Node {
int key; //键
int value;//值
Node prev;//前驱
Node next;//后继
/**
* 无参构造
*/
public Node() {
}
/**
* 有参构造
*
* @param key 健
* @param value 值
*/
public Node(int key, int value) {
this.key = key;
this.value = value;
}
}
}
测试
java
//实例一个缓存大小为7的LRU缓存
LRUCache lruCache =new LRUCache(5);
lruCache.put(1,1);
lruCache.put(2,2);
lruCache.put(3,3);
lruCache.put(4,4);
lruCache.put(5,5);
lruCache.put(6,6);
System.out.println("依次存入1、2、3、4、5、6后的缓存:"+lruCache);
int l1 = lruCache.get(1);
System.out.println("取出1后的缓存:"+lruCache+",取出的值:"+l1);
int l2 = lruCache.get(2);
System.out.println("取出2后的缓存:"+lruCache+",取出的值:"+l2);
int l3 = lruCache.get(3);
System.out.println("取出3后的缓存:"+lruCache+",取出的值:"+l3);
lruCache.put(9,9);
System.out.println("存入9后的缓存:"+lruCache);
测试结果