一、什么是LRU
在了解什么是LRU算法时,得先清楚软件有哪些缓存,软件缓存分为:
1、内存缓存;
2、数据库缓存;
3、网络缓存;
速度依次减慢,缓存到内存中的数据,长时间可能会有一些无用的数据占用内存甚至导致内存雪崩的发生,为了解决这些,产生了LRU(Least Recently Used)算法。LRU算法只是解决问题的其中一种,还有包括FIFO(First In, First Out),LFU(Least Frequently Used)算法。这里我们只讲LRU算法的实现。
二、LRU算法内容
1、新数据插入到链表头部;
2、当缓存命中(即缓存数据被访问),数据要移到表头;
3、当链表满的时候,将链表尾部的数据丢弃
二、LRU算法实现
根据描述,我们采用单链表的形式来实现该需求:
(1)先实现单链表
1 //单链表 2 public class LinkedList<T> { 3 4 Node list; //链表的头节点 5 int size; //链表有多少个节点 6 7 public LinkedList() { 8 size = 0; 9 } 10 11 //添加节点 12 //在头部添加节点 13 public void put(T data) { 14 Node head = list; 15 Node curNode = new Node(data, list); //构建新节点,同时将该节点的下一个节点指向list 16 list = curNode; //更新头节点为新节点 17 size++; 18 } 19 20 //在链表的index 位置插入一个新的数据data 21 public void put(int index,T data) { 22 checkPositionIndex(index); 23 Node cur = list; 24 Node head = list; 25 for(int i = 0; i < index; i++) { 26 head = cur; 27 cur = cur.next; 28 } 29 Node node = new Node(data, cur); //构建新节点,下一个节点指向原来的index节点 30 head.next = node; //将index-1节点的下一个节点指向新构建的节点 31 size++; 32 33 } 34 35 //删除节点 36 //删除头部节点 37 public T remove() { 38 if (list != null) { 39 Node node = list; //获取头节点 40 list = list.next; //将头节点指向原本头节点的下一个节点 41 node.next = null; // GC 回收 将原本的头节点的下一个节点指向设为null 42 size--; 43 return node.data; 44 } 45 return null; 46 } 47 48 public T remove(int index) { //删除指定下标位置的节点 49 checkPositionIndex(index); 50 Node head = list; 51 Node cur = list; 52 for(int i = 0; i < index; i++) { 53 head = cur; 54 cur = cur.next; 55 } 56 head.next = cur.next; 57 cur.next = null;//GC 58 size--; 59 return cur.data; 60 } 61 62 public T removeLast() { //删除最后一个节点 63 if (list != null) { 64 Node node = list; 65 Node cur = list; 66 while(cur.next != null) { 67 node = cur; 68 cur = cur.next; 69 } 70 node.next = null; 71 size--; 72 return cur.data; 73 74 } 75 return null; 76 } 77 //修改index位置的节点数据 78 public void set(int index,T newData) { 79 checkPositionIndex(index); 80 Node head = list; 81 for(int i = 0; i < index; i++) { 82 head = head.next; 83 } 84 head.data = newData; 85 } 86 87 //查询节点 88 //get 头部节点 89 public T get() { 90 Node node = list; 91 if (node != null) { 92 return node.data; 93 } else { 94 return null; 95 } 96 } 97 98 public T get(int index) { //获取下标index处的节点 99 checkPositionIndex(index); 100 Node node = list; 101 for(int i = 0; i < index; i++) { 102 node = node.next; 103 } 104 return node.data; 105 } 106 107 //检测index是否在链表范围以内 108 public void checkPositionIndex(int index) { 109 if(!(index >=0 && index <=size)) { 110 throw new IndexOutOfBoundsException("index: " + index + ", size: " + size); 111 } 112 113 } 114 115 //节点的信息 116 class Node { 117 T data; 118 Node next; 119 120 public Node(T data,Node node) { 121 this.data = data; 122 this.next = node; 123 } 124 } 125 }
(2)实现LRU缓存回收算法
1 public class LruLinkedList<T> extends LinkedList<T> { 2 3 int memory_size; // 用于限定内存空间大小,也就是缓存的大小,这里模拟大小 4 static final int DEFAULT_CAP = 5; //默认5个大小 5 6 public LruLinkedList() { 7 this(DEFAULT_CAP); 8 } 9 10 public LruLinkedList(int default_memory_size) { 11 memory_size = default_memory_size; 12 } 13 14 //LRU添加节点 15 public void lruPut(T data) { 16 if (size >= memory_size) { 17 removeLast(); 18 put(data); 19 } else { 20 put(data); 21 } 22 } 23 24 //LRU删除 25 public T lruRemove(){ 26 return removeLast(); 27 } 28 29 //LRU访问 30 public T lruGet(int index) { 31 checkPositionIndex(index); 32 Node node = list; 33 Node pre = list; 34 for(int i = 0; i < index; i++) { 35 pre = node; 36 node = node.next; 37 } 38 T resultData = node.data; 39 //将访问的节点移到表头 40 pre.next = node.next; 41 Node head = list; 42 node.next = head; 43 list = node; 44 return resultData; 45 } 46 47 public static void main(String[] args) { 48 LruLinkedList<Integer> lruLinkedList = new LruLinkedList<>(5); 49 for(int i = 0; i <4; i++) { 50 lruLinkedList.lruPut(i); 51 } 52 lruLinkedList.lruGet(2) 53 lruLinkedList.lruPut(12); 54 lruLinkedList.lruPut(76); 55 } 56 }
到这里整个LRU算法就算模拟完成。