谈谈LRU算法

339 阅读2分钟

这是我参与更文挑战的第1天,活动详情查看: 更文挑战

LRU原理

LRU:(least recently used)最近最少使用算法,它是一种缓存逐出策略 cache eviction policies,使用场景是当内存不足时,需要淘汰最近最少使用的数据。 LRU算法是假设最近最少使用的那些数据,将来被使用的概率也不大,当内存有限的情况下,就可以把这些不常用的信息淘汰掉。比如有爆款商品时,大家都在搜索这个信息,那刚搜素的信息接下来被搜索的概率也大,就比不常用的一些商品被搜索的概率大,所以需要把很久没有用过的数据踢出去,也就是 Least Recently Used 的信息被踢出去。就是保留热点数据(最近最多使用的数据)。利用LRU我们可以解决很多实际开发中的问题,并且很符合业务场景。算法根据数据的历史访问记录来进行淘汰数据,它的主要衡量指标是使用的时间,附加指标是使用的次数。

算法分析与实现:

采用HashMap + Doubly Linked List实现。 Cache对象,需要规定缓存的容量,在初始化时,设置容量大小,然后实例化双向链表的head,tail,链表是双路的,我们定义好头节点和尾节点,然后利用先进先出(FIFO),最近被放入的数据会最早被获取 HahsMap用于快速查找到结点所在位置,然后将使用到的结点放在对头,这样最近最少使用的结点自然就落入到队尾。

public class LRUCache {
  //双向链表内
  public static class Node {
      int key;
      int val;
      Node next;
      Node prev;
      public Node(int key, int val) {
          this.key = key;
          this.val = val;
      }
  }


  //Node记录表
  Map<Integer, Node> map = new HashMap<>();
  
  //双向链表头部
  private Node head;
  
  //双向链表尾部
  private Node tail;
  
  //容量
  private int cap;

  public LRUCache(int capacity) {
      cap = capacity;
  }

  public int get(int key) {
      Node node = map.get(key);
      //如果Node不在表中,代表缓存中并没有
      if(node == null) {
          return -1;
      } else {
      //如果存在,则需要移动Node节点到表头
          int res = node.val;
          remove(node);
          appendHead(node);
          return res;
      }
  }

  public void put(int key, int value) {
      Node node = map.get(key);
      //如果存在,则需要移动Node节点到表头
      if(node != null) {
          node.val = value;
          remove(node);
          appendHead(node);
      } else {
      //如果Node不在表中,代表缓存中并没有
          node = new Node(key, value);
          if(map.size() < cap) {
              appendHead(node);
              map.put(key, node);
          } else {
              // 踢走老的
              map.remove(tail.key);
              remove(tail);
              appendHead(node);
              map.put(key, node);
          }
      }
  }

  private void appendHead(Node node) {
      if(head == null) {
          head = tail = node;
      } else {
          node.next = head;
          head.prev = node;
          head = node;
      }
  }

  private void remove(Node node) {
      if(head == tail) {
          head = tail = null;
      } else {
          if(head == node) {
              head = head.next;
              node.next = null;
          } else if (tail == node) {
              tail = tail.prev;
              tail.next = null;
              node.prev = null;
          } else {
              node.prev.next = node.next;
              node.next.prev = node.prev;
              node.prev = null;
              node.next = null;
          }
      }
  }

}