(一)定义
LRU是Least Recently Used的缩写,即最近最少使用、选择最久未使用的数据予以淘汰,它的目的是为了解决数据缓存空间不足时淘汰数据的顺序
(二)原理
基于单向链表实现的LRU算法,它的实现特点如下
1、添加新数据时添加在链表的头部
2、当数据命中,数据要移动到表头(命中:链表中的数据被访问)
3、(添加数据时)链表的缓存空间满时,将链表尾部的数据丢弃
(三)代码实现(基于单向链表)
LRUList.java
/**
* 单向链表
*/
public class LRUList<T> {
private Node headNode; //头节点
private int size; //节点数
public LRUList() {
size = 0;
}
//再链头添加新的节点
public void add(T data) {
Node newNode = new Node(data, headNode);
headNode = newNode;
size++;
}
//查询节点
public T get(int index) {
checkPositionIndex(index);
if (headNode != null) {
Node target = headNode;
for (int i = 0; i < index; i++) {
target = target.next;
}
return target.mData;
}
return null;
}
//删除节点
public boolean remove(int index) {
checkPositionIndex(index);
if (headNode != null) {
Node head = headNode;
Node target = headNode;
for (int i = 0; i < index; i++) {
head = target;
target = target.next;
}
head.next = target.next;
target.mData = null;
target = null;
size--;
return true;
}
return false;
}
public boolean removeLast() {
if (headNode != null) {
Node head = headNode;
Node target = headNode;
while (target.next != null) {
head = target;
target = target.next;
}
head.next = null; //链尾
target.mData = null; //gc
size--;
return true;
}
return false;
}
//判断下标是否符合条件
public void checkPositionIndex(int index) {
if (index < 0 || index >= size) throw new IndexOutOfBoundsException("下标溢出");
}
//toString()方法
@Override
public String toString() {
Node head = headNode;
while (head.next != null) {
System.out.print(head.mData + " ");
head = head.next;
}
System.out.println();
return super.toString();
}
//返回链表的节点数
public int size() {
return size;
}
//获取头节点
public Node getHead() {
if (headNode != null)
return headNode;
return null;
}
//设置头节点
public void setHead(Node node) {
this.headNode = node;
}
//节点
class Node {
private T mData;
private Node next;
public Node(T mData, Node mNode) {
this.mData = mData;
this.next = mNode;
}
}
}
LRU算法表:
LRUDemo.java
public class LRUDemo<T> extends LRUList<T> {
private int size; //缓存大小
private static final int DEFAULT_SIZE = 5; //默认缓存大小
public LRUDemo() {
this(DEFAULT_SIZE);
}
public LRUDemo(int size) {
this.size = size;
}
//添加节点
public void LRUAdd(T data) {
if (size < size())
removeLast(); //如果链表中的的个数大于最大缓存
add(data); //链表中的add方法
}
//查看节点
public T LRUget(int index) {
checkPositionIndex(index); //链表中的下标判断方法
T data = get(index);
remove(index);
Node node = new Node(data, getHead());
setHead(node);
return data;
}
//删除节点
public boolean LRURemove() {
if (getHead() != null) {
removeLast();
return true;
}
return false;
}
public static void main(String[] args) {
LRUDemo lru = new LRUDemo();
for (int i = 0; i < 6; i++) {
lru.LRUAdd("data"+i);
}
System.out.print("添加的数据:"+" ");
lru.toString();
System.out.print("缓存满时添加数据后的显示:"+" ");
lru.LRUAdd(2520);
lru.toString();
System.out.print("命中(index = 2)数据后的显示:"+" ");
lru.LRUget(2);
lru.toString();
lru.LRURemove();
System.out.print("URL删除数据后的显示:"+" ");
lru.toString();
}
}
结果截图:
由于是基于单向链表的LRU算法,所以在实现LRU算法时,我们先要实现单向链表(单向链表的具体解析点击进入),在LRU算法代码中,有两个构造方法LRUDemo()、LRUDemo(int size)
,它们分别是设置缓存大小的默认值和自定义缓存值,基于LRU算法的原理,添加节点 LRUAdd(T data)
时,我们要先判断单向链表中的个数是否大于LRU缓存的大小(size < size())
,如果大于先删除链尾的节点再往链头上添加节点,否则直接往链头上添加节点;在LRUget(int index)
(查看节点)中,我们先将目的节点删除(remove(index)
);,再将这个节点添加到链头(Node node = new Node(data, getHead());
)并设置链头的指向节点(setHead(node);
);由于LRU算法的删除是在链尾中进行的,因此我们只需直接调用单向链表中的removeLast();
方法即可。
ok,lru算法的讲解到此就结束了,如果对你有帮助的话👍一个哦 (*^_^*)
(在手机看可能代码显示不全,可以转到电脑上查看,谢谢)