LRU算法
LRU算法是什么
- 最近最久未使用
LRU的实现
利用双向链表实现
双向链表的特点就是他的链表是双路的,定义好头结点和尾节点后,利用先进先出(FIFO),最近被放入的数据会最早被获取
-
java实现
-
定义基本的链表操作节点
public class Node { //键 Object key; //值 Object value; //上一个节点 Node pre; //下一个节点 Node next; public Node(Object key, Object value) { this.key = key; this.value = value; } }
-
链表基本定义
- 定义一个LRU类,然后定义它的大小,容量,头节点,尾节点等部分,然后定义一个基本的构造方法
public class LRU<K, V> { //当前大小 private int currentSize; //总容量 private int capcity; //所有的node节点 private HashMap<K,Node> caches; //头结点 private Node first; //尾节点 private Node last; public LRU(int size){ currentSize=0; this.capcity=size; caches=new HashMap<K,Node>(size); }
-
添加元素
- 首先先判断是否已经存在,如果是新元素,判断当前大小是否大于等于总容量,防止因为添加导致超出表大小,最后将添加的节点移动到头部。
public void put(K key,V value){ //先看看里面是否存在 Node node =caches.get(key); //如果是新元素 if(node==null){ //判断是否超过最大容量 if(caches.size()>=capcity){ //将最后一个节点删除 caches.remove(last.key); removeLast(); } //创建新节点 node=new Node(key,value); } //如果存在就覆盖旧值 node.value=value; //把元素移到首部 moveToHead(node); caches.put(key,node); }
-
访问元素
- 先判断是否存在,存在的话,先把数据移到头部节点,然后返回旧值。
public Object get (K key){ Node node = caches.get(key); if(node==null){ return null; }else { //把访问节点移到首节点 moveToHead(node); return node; } }
-
节点删除操作
- 在删除的时候需要注意的是,把前后节点的指针给进行相应的更改
public Object remove(K key){ Node node= caches.get(key); if(node != null){ if(node.pre!=null){ node.pre.next=node.next; } if (node.next!=null){ node.next.pre=node.pre; } if(node==first){ first=node.next; } if(node==last){ last=node.pre; } } return caches.remove(key); }
-
移动元素到头节点
- 首先把当前节点移除(类似于删除),然后将其放到首节点
private void moveToHead(Node node) { if (first == node) { return; } if (node.next != null) { node.next.pre = node.pre; } if (node.pre != null) { node.pre.next = node.next; } if (node == last) { last = last.pre; } if (first == null || last == null) { first = last = node; return; } node.next = first; first.pre = node; first = node; first.pre = null; }
-
-
LRU完成代码
import java.util.HashMap; public class LRU<K, V> { //当前大小 private int currentSize; //总容量 private int capcity; //所有的node节点 private HashMap<K,Node> caches; //头结点 private Node first; //尾节点 private Node last; public LRU(int size){ currentSize=0; this.capcity=size; caches=new HashMap<K,Node>(size); } /** * @Author: sun mingzhi * @Description: 添加元素 * @Date: 2020/1/19 10:06 * @Return: [key, value] */ public void put(K key,V value){ //先看看里面是否存在 Node node =caches.get(key); //如果是新元素 if(node==null){ //判断是否超过最大容量 if(caches.size()>=capcity){ //将最后一个节点删除 caches.remove(last.key); removeLast(); } //创建新节点 node=new Node(key,value); } //如果存在就覆盖旧值 node.value=value; //把元素移到首部 moveToHead(node); caches.put(key,node); } /** * @Author: sun mingzhi * @Description: 通过key获取元素 * @Date: 2020/1/19 10:40 * @Return: */ public Object get (K key){ Node node = caches.get(key); if(node==null){ return null; }else { //把访问节点移到首节点 moveToHead(node); return node; } } /** * @Author: sun mingzhi * @Description: 通过key删除元素 * @Date: 2020/1/19 10:42 * @Return: */ public Object remove(K key){ Node node= caches.get(key); if(node != null){ if(node.pre!=null){ node.pre.next=node.next; } if (node.next!=null){ node.next.pre=node.pre; } if(node==first){ first=node.next; } if(node==last){ last=node.pre; } } return caches.remove(key); } /** * 清除所有节点 */ public void clear() { first = null; last = null; caches.clear(); } /** * 把当前节点移动到首部 * @param node */ private void moveToHead(Node node) { if (first == node) { return; } if (node.next != null) { node.next.pre = node.pre; } if (node.pre != null) { node.pre.next = node.next; } if (node == last) { last = last.pre; } if (first == null || last == null) { first = last = node; return; } node.next = first; first.pre = node; first = node; first.pre = null; } /** * 移除最后一个节点 */ private void removeLast() { if (last != null) { last = last.pre; if (last == null) { first = null; } else { last.next = null; } } } @Override public String toString() { StringBuilder sb = new StringBuilder(); Node node = first; while (node != null) { sb.append(String.format("%s:%s ", node.key, node.value)); node = node.next; } return sb.toString(); } public static void main(String[] args) { LRU<Integer, String> lru = new LRU<Integer, String>(5); lru.put(1, "a"); lru.put(2, "b"); lru.put(3, "c"); lru.put(4,"d"); lru.put(5,"e"); System.out.println("原始链表为:"+lru.toString()); lru.get(4); System.out.println("获取key为4的元素之后的链表:"+lru.toString()); lru.put(6,"f"); System.out.println("新添加一个key为6之后的链表:"+lru.toString()); lru.remove(3); System.out.println("移除key=3的之后的链表:"+lru.toString()); } }
利用LinkedHashMap实现
LinkedHashMap底层就是使用HashMap加双链表实现的,而且本身已经实现了按照访问顺序存储,此外LinkedHashMap本身就实现了一个removeEldestEnter用于判断是否需要移除最不常读取的数,方法默认返回false,不会移除元素,只需要重写此方法即可。
import java.util.LinkedHashMap;
import java.util.Map;
public class LRU<K, V> {
private static final float hashLoadFactory = 0.75f;
private LinkedHashMap<K,V> map;
private int cacheSize;
public LRU(int cacheSize) {
this.cacheSize = cacheSize;
int capacity = (int)Math.ceil(cacheSize / hashLoadFactory) + 1;
//true 表示让LinkedHashMap按照访问顺序来进行排序,最近访问的放在头部,最后访问的放在尾部
map = new LinkedHashMap<K,V>(capacity, hashLoadFactory, true){
private static final long serialVersionUID = 1;
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
//当map中的数据量大于某个指定的缓存个数的时候,就自动删除最老的元素
return size() > LRU.this.cacheSize;
}
};
}
public synchronized V get(K key) {
return map.get(key);
}
public synchronized void put(K key, V value) {
map.put(key, value);
}
public synchronized void clear() {
map.clear();
}
public synchronized int usedSize() {
return map.size();
}
public void print() {
for (Map.Entry<K, V> entry : map.entrySet()) {
System.out.print(entry.getValue() + "--");
}
System.out.println();
}
public static void main(String[] args) {
LRU<Integer, String> lru = new LRU<Integer, String>(5);
lru.put(1, "a");
lru.put(2, "b");
lru.put(3, "c");
lru.put(4,"d");
lru.put(5,"e");
System.out.println("原始链表为:");
lru.print();
System.out.println("获取key为4的元素之后的链表:");
lru.get(4);
lru.print();
System.out.println("新添加一个key为6之后的链表:");
lru.put(6,"f");
lru.print();
}
}
来源:CSDN
作者:冢狐
链接:https://blog.csdn.net/issunmingzhi/article/details/104040285