LRU即是Least Recently Used,即最近最少使用,选择最近最久未使用的数据将其淘汰。
最简单的想法是使用先进先出(FIFO)的方式来实现,通过双向链表来实现
因为链表插入和删除快,但是查询慢 ,而 HashMap的查询速度快很多,所以除了构建一个双向链表,还构建了一个哈希map,用于快速查询对应key的节点,用空间换时间
每次key被访问的时候,把被访问到的key移到头节点,这样当添加新节点的时候,直接添加到头节点即可,如果缓存已满,直接删除尾节点就可以了
jdk里有提供现成的LinkedHashMap,采用HashMap+链表的方式自己实现了一下
/**
* @Description
* @Author chenpp
* @Date 2019/11/21 17:50
* 定义双向链表的节点
*/
public class Node<K,V> {
private K key;
private V value;
private Node next;
private Node pre;
public Node(K key,V value){
this.key = key;
this.value = value;
}
public K getKey() {
return key;
}
public void setKey(K key) {
this.key = key;
}
public V getValue() {
return value;
}
public void setValue(V value) {
this.value = value;
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
public Node getPre() {
return pre;
}
public void setPre(Node pre) {
this.pre = pre;
}
}
/**
* @Description
* @Author chenpp
* @Date 2019/11/21 17:56
* 定义一个双向链表
*/
public class DoubleLinkedList<K,V> {
transient Node<K,V> first;
transient Node<K,V> last;
/**
* 当前链表里的节点数目
* */
transient int size;
/**
* 添加节点到链表头部
* */
public void addFirst(Node node) {
if (first == null) {
first = node;
last = node;
} else {
Node oldFirst = first;
oldFirst.setPre(node);
node.setNext(oldFirst);
first = node;
}
size++;
}
/**
* 删除链表里已有的node节点
*
* */
private Node removeNode(Node node){
if(node == null){
return null;
}
Node preNode = node.getPre();
Node nextNode = node.getNext();
if(preNode != null) {
preNode.setNext(nextNode);
}else{
first = nextNode;
}
if(nextNode != null){
nextNode.setPre(preNode);
}else{
last = preNode;
}
size--;
return node;
}
/**
* 将链表指定节点node移动到头部
*
* */
public Node moveToHead(Node node){
if(node == first){
return node;
}
if( node == last){
last = node.getPre();
}else{
node.getNext().setPre(node.getPre());
}
node.getPre().setNext(node.getNext());
node.setPre(null);
node.setNext(first);
first = node;
return node;
}
/**
* 删除最后一个节点
* 并返回删除的节点
*
* */
public Node removeLast(){
Node node = last;
removeNode(last);
return node;
}
public int size(){
return size;
}
}
/**
* @Description
* @Author chenpp
* @Date 2019/11/21 17:52
*
*/
public class LRUCache<K,V> {
private HashMap<K, Node<K,V>> linkedHashMap;
private int capacity;
private DoubleLinkedList linkedList;
public LRUCache(int capacity){
this.capacity = capacity;
linkedList = new DoubleLinkedList();
linkedHashMap = new HashMap<K, Node<K,V>>();
}
/**
* 访问缓存指定的key
* 将对应的node移动到链表头部
*
* */
public V get(K k){
Node<K,V> node = linkedHashMap.get(k);
if(node == null){
return null;
}
//将访问的节点移动到头节点
linkedList.moveToHead(node);
return node.getValue();
}
/**
* 新增node节点到链表头部
* 如果缓存超过最大容量,则删除尾部节点
*
* */
public void put(K key,V val){
Node node = linkedHashMap.get(key);
//如果节点的key本来就存在,则直接移动到头节点
if(node != null){
node.setValue(val);
node = linkedList.moveToHead(node);
}else{
node = new Node(key,val);
//如果key不存在,则新增节点到头部
if(linkedList.size() >= capacity){
//移除访问时间最远的节点
Node last = linkedList.removeLast();
linkedHashMap.remove(last.getKey());
}
linkedList.addFirst(node);
}
//更新hashMap里的值
linkedHashMap.put(key,node);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
Node node = linkedList.first;
while(node != null){
sb.append(String.format("%s:%s ", node.getKey(),node.getValue()));
node = node.getNext();
}
return sb.toString();
}
}
测试一下:
/**
* @Description
* @Author chenpp
* @Date 2019/11/21 19:49
*/
public class Test {
public static void main(String[] args) {
LRUCache<String,String> cache = new LRUCache<String, String>(5);
cache.put("a","111");
cache.put("b","2121");
cache.put("c","3231");
cache.put("d","2122");
cache.put("e","2131");
cache.get("3");//e d c b a
System.out.println(cache.toString());
cache.get("a");// a e d c b
System.out.println(cache.toString());
cache.put("f","13123");// f a e d c
System.out.println(cache.toString());
cache.get("b");
System.out.println(cache.toString());
cache.get("a");// a f e d c
System.out.println(cache.toString());
cache.put("g","g");// g a f e d
System.out.println(cache.toString());
cache.put("c","31");// c g a f e
System.out.println(cache.toString());
cache.get("d");
System.out.println(cache.toString());
cache.get("c");
System.out.println(cache.toString());
}
}