LRU算法

引至【想不到!面试官问我:Redis 内存满了怎么办?】,本文只关注其中的LRU算法

LRU(Least Recently Used),即最近最少使用,是一种缓存置换算法。在使用内存作为缓存的时候,缓存的大小一般是固定的。当缓存被占满,这个时候继续往缓存里面添加数据,就需要淘汰一部分老的数据,释放内存空间用来存储新的数据。这个时候就可以使用LRU算法。其核心思想是:如果一个数据在最近一段时间没有被用到,那么将来被使用到的可能性也很小,所以就可以被淘汰掉。

使用java实现一个简单的LRU算法

主要思想:LRU内部实现一个Node<k,v>类,该类包含key、value,前一节点pre和后一节点next四个属性;LRU中包含一个Map<k, Node<k, v>> nodeMap的缓存map,包含所有的数据,哨兵 Node<k, v> head和 Node<k, v> tail标注链表的头部和尾部。当存入数据时(先判断LRU是否已超长,如果超长则需要删除数据)判断nodeMap是否包含该节点,如果不包含,则将该节点添加至nodeMap中,并将该节点插入至head后(即该节点pre设置为head,head节点的next设置为当前节点,该节点的next设置为head节点的的next);如果包含该数据,则需要将该node从原链表中删除,然后再将该数据插入至head后即可(将该节点的pre设置为head,head节点的next设置为当前节点)。当数据被get时,也需要先将该node从原链表中删除,然后再将该数据插入至head后(将该节点的pre设置为head,head节点的next设置为当前节点),即实现了LRU.

  1 public class LRUCache<k, v> {
  2     //容量
  3     private int capacity;
  4     //当前有多少节点的统计
  5     private int count;
  6     //缓存节点
  7     private Map<k, Node<k, v>> nodeMap;
  8     private Node<k, v> head;
  9     private Node<k, v> tail;
 10 
 11     public LRUCache(int capacity) {
 12         if (capacity < 1) {
 13             throw new IllegalArgumentException(String.valueOf(capacity));
 14         }
 15         this.capacity = capacity;
 16         this.nodeMap = new HashMap<>();
 17         //初始化头节点和尾节点,利用哨兵模式减少判断头结点和尾节点为空的代码
 18         Node headNode = new Node(null, null);
 19         Node tailNode = new Node(null, null);
 20         headNode.next = tailNode;
 21         tailNode.pre = headNode;
 22         this.head = headNode;
 23         this.tail = tailNode;
 24     }
 25 
 26     public void put(k key, v value) {
 27         Node<k, v> node = nodeMap.get(key);
 28         if (node == null) {
 29             if (count >= capacity) {
 30                 //先移除一个节点
 31                 removeNode();
 32             }
 33             node = new Node<>(key, value);
 34             //添加节点
 35             addNode(node);
 36         } else {
 37             //移动节点到头节点
 38             moveNodeToHead(node);
 39         }
 40     }
 41 
 42     public Node<k, v> get(k key) {
 43         Node<k, v> node = nodeMap.get(key);
 44         if (node != null) {
 45             moveNodeToHead(node);
 46         }
 47         return node;
 48     }
 49 
 50     private void removeNode() {
 51         Node node = tail.pre;
 52         //从链表里面移除
 53         removeFromList(node);
 54         nodeMap.remove(node.key);
 55         count--;
 56     }
 57 
 58     private void removeFromList(Node<k, v> node) {
 59         Node pre = node.pre;
 60         Node next = node.next;
 61 
 62         pre.next = next;
 63         next.pre = pre;
 64 
 65         node.next = null;
 66         node.pre = null;
 67     }
 68 
 69     private void addNode(Node<k, v> node) {
 70         //添加节点到头部
 71         addToHead(node);
 72         nodeMap.put(node.key, node);
 73         count++;
 74     }
 75 
 76     private void addToHead(Node<k, v> node) {
 77         Node next = head.next;
 78         next.pre = node;
 79         node.next = next;
 80         node.pre = head;
 81         head.next = node;
 82     }
 83 
 84     public void moveNodeToHead(Node<k, v> node) {
 85         //从链表里面移除
 86         removeFromList(node);
 87         //添加节点到头部
 88         addToHead(node);
 89     }
 90 
 91     class Node<k, v> {
 92         k key;
 93         v value;
 94         Node pre;
 95         Node next;
 96 
 97         public Node(k key, v value) {
 98             this.key = key;
 99             this.value = value;
100         }
101     }
102 }

 

posted @ 2019-10-09 09:14  CoderZZZ  阅读(336)  评论(0编辑  收藏  举报