什么是LRU缓存淘汰算法?JAVA在链表中使用指针简单实现
什么是LRU缓存淘汰算法?
链表的三种策略
- 先进先出策略 FIFO(First In,FirstOut)
- 最少使用策略 LFU(Least Frequently Used)
- 最近最少使用策略 LRU(LeastRecently Used)
也就是将最近最少使用的数据进行淘汰删除。
我们维护一个有序单链表,越靠近链表尾部的结点是越早之前访问的。当有一个新的数据被访问时,我们从链表头开始顺序遍历链表。
- 如果此数据之前已经被缓存在链表中了,我们遍历得到这个数据对应的结点,并将其从原来的位置删除,然后再插入到链表的头部。
- 如果此数据没有在缓存链表中,又可以分为两种情况:
如果此时缓存未满,则将此结点直接插入到链表的头部;
如果此时缓存已满,则链表尾结点删除,将新的数据结点插入链表的头部。
简单的一个实现
我在本地写了一个具有少量方法的简单实现,没有引入其他的jar,只是使用自定义的就可以完成。
当写一些底层代码的时候,最好不要有任何依赖,用数组,自定义类,引用指向的转换来解决问题,这样可以有效的提高时间效率。
package service.jmw.weekfour;
/**
* 实现一个 LRU算法 链表
*
* @author JMWANG
*/
public class MyLinkedList<T> {
//最大容量
private transient static int capacity = 0;
//真实容量
private transient static int realTotal = 0;
// ture 为 主动 (主动扩容的话,会处罚LRU算法标记垃圾 并且清除垃圾)
// 默认false 被动扩容
private transient static boolean expansionMode = false;
//LRU算法删除标记位 从前往后删除
private transient static int deleteLogo = 0;
/**
* 构造方法
*/
public MyLinkedList(int capacity,boolean expansionMode) {
this.capacity = capacity;
this.expansionMode = expansionMode;
}
public MyLinkedList(int capacity) {
this.capacity = capacity;
}
public MyLinkedList() {
this.capacity = 16;
}
private static Node node = null;
/**
* 添加
*/
public void add(T t) {
Node temp = node;
if (realTotal >= capacity) {
if (!expansionMode) {
passiveExpansion();
while (temp.next != null) {
temp = temp.next;
}
Node<T> tNode = new Node<T>(null,null,t);
temp.next = tNode;
}else {
//LRU Least Recently Used算法
for (int j = 0; j < deleteLogo; j++) {
temp = temp.next;
}
node = temp;
}
} else {
if (node == null) {
Node<T> tNode = new Node<T>(null,null,t);
node = tNode;
} else {
while (temp.next != null) {
temp = temp.next;
}
Node<T> tNode = new Node<T>(null,null,t);
temp.next = tNode;
}
}
realTotal++;
}
/**
* 插入
*/
public void insert(int i,T t) {
Node temp = node;
if (realTotal >= capacity) {
if (!expansionMode) {
passiveExpansion();
int k = 1;
while (temp.next!=null){
if (i == k){
Node<T> nodeT = new Node<T>(null,null,t);
nodeT.next = temp.next; //.next
temp.next = nodeT;
realTotal++;
break;
}
temp = temp.next;
k++;
}
}else {
//LRU Least Recently Used算法
for (int j = 0; j < deleteLogo; j++) {
temp = temp.next;
}
node = temp;
}
} else {
int k = 1;
while (temp.next!=null){
if (i == k){
Node<T> nodeT = new Node<T>(null,null,t);
nodeT.next = temp.next; //.next
temp.next = nodeT;
realTotal++;
break;
}
temp = temp.next;
k++;
}
}
}
/**
* 删除
*/
public void delete(T t) {
Node temp = node;
if (temp.value.equals(String.valueOf(t))){}else if (realTotal!=0){
while (temp.next!=null){
if (temp.next.value.equals(String.valueOf(t))){
temp.next = temp.next.next;
break;
}
temp = temp.next;
}
}
if (expansionMode){
node = new Node(node,null,t);
deleteLogo++;
}else {
realTotal--;
}
}
/**
* 查询
*/
public String get() {
Node nodeTemp = new Node(null,null,null);
Object[] ts = new Object[realTotal];
ts[0] = node.value;
nodeTemp = node.next;
//循环处理
for (int i = 1; i < realTotal; i++) {
ts[i] = nodeTemp.value;
nodeTemp = nodeTemp.next;
}
Object[] tsTemp = new Object[realTotal-deleteLogo];
for (int i = deleteLogo,j=0; i < ts.length; i++,j++) {
tsTemp[j] = ts[i];
}
//返回字符串
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < tsTemp.length; i++) {
if (i == 0 && tsTemp.length == 1) {
stringBuilder.append("[" + ts[i] + "]");
break;
}
if (i == tsTemp.length - 1) {
stringBuilder.append(tsTemp[i] + "]");
} else if (i == 0) {
stringBuilder.append("[" + tsTemp[i] + ",");
} else {
stringBuilder.append(tsTemp[i] + ",");
}
}
return stringBuilder.toString();
}
/**
* 主动扩容 随机数
*/
public void capacityExpansion(int expansionMultiple) {
capacity = capacity + expansionMultiple;
}
/**
* 被动扩容 2倍
*/
public void passiveExpansion() {
capacity = capacity * 2;
}
/**
* 长度
*/
public int size(){
return realTotal;
}
static class Node<T> {
private Node next;
private Node pri;
private T value;
public Node(Node next,Node pri,T value){
this.next = next;
this.pri = pri;
this.value = value;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Node)) return false;
Node<?> node = (Node<?>) o;
return value != null ? value.equals(node.value) : node.value == null;
}
@Override
public int hashCode() {
return value != null ? value.hashCode() : 0;
}
@Override
public String toString() {
return "Node{" +
"next=" + next +
", value=" + value +
'}';
}
}
public static void main(String[] args) {
MyLinkedList linkedList = new MyLinkedList(5);
linkedList.add("4");
linkedList.add("5");
linkedList.add("1");
linkedList.add("5");
System.out.println(linkedList.get());
linkedList.insert(2,"9");
System.out.println(linkedList.get());
linkedList.delete(5);
System.out.println(linkedList.get());
linkedList.add("100");
System.out.println(linkedList.get());
linkedList.add("100");
System.out.println(linkedList.get());
}
}
总结
当然这并不是最好的方式,在空间时间等地方可以优化,并且这只是单项的链表,可以在功能上扩展修改为双向,甚至环。
继续优化这个实现思路,比如引入散列表(Hash table)来记录每个数据的位置,将缓存访问的时间复杂度降到 O(1)。
如有错误欢迎指正