1_使用LinkedHashMap实现LRU

  • 参考 https://www.cnblogs.com/lzrabbit/p/3734850.html

  • LRU的整体思路是:淘汰最长时间未使用的页面(key)

  • 一种简单的实现思路是:

    维持一个链表,把刚被插入的节点放在链表尾;刚被访问的节点脱链,也放到链表尾;每次需要淘汰节点的时候就淘汰链表头

    (所以,其实是一个双链表比较好)

  • 但是上面思路的问题在于,如果链表很长,那么更新被访问节点的位置时,最坏的情况下要扫描整个链表;怎样解决这个问题呢?没错,是不是可以结合Java中的HashMap,先把链表分桶,减少扫描长度

  • 幸运的是Java中已经有 LinkdedHashMap实现了类似的事情

    (1) LinkedHashMap整体结构和实现原理都和HashMap很像,不同之处在于它整体是一个双向链表:虽然分了桶,但是每个结点都会记录自己的上一个结点和下一个结点

    (2) LinkedHashMap在构造函数中可以指定链接方式:一种是按照插入的顺序组织成双链表;一种是根据访问的顺序组织成双链表,维持链表头head是最长时间没有访问的结点。很显然,实现LRU用到的应该是后一种方式

    (3) 如果不做任何操作的话,LinkedHashMap和HashMap一样容量不够就扩容。所以要重写 removeEldestEntry 方法(默认是return false)

      @Override
      protected boolean removeEldestEntry(Map.Entry eldest) {
    
          return size() > LRU.this.cacheSize;
      }
    

    这样写的话,容量不够就会淘汰 oldest 的页面,从而跟LRU的思路是一致的

    (4) 和 HashMap 一样, LinkedHashMap 同样非线程安全,一种线程安全的实现方式是“客户端加锁”。以下是一种LRU利用LinkedHashMap的多线程实现

      import java.util.LinkedHashMap;
      import java.util.Map;
    
      class LRU<K, V> {
    
          public static void main(String[] args) {
    
              LRU<Integer, Integer> lru = new LRU<>(5);
    
              for (int i = 0; i < 5; i++) {
                  lru.put(i, i);
              }
    
              //lru.get(0);
    
              lru.put(5, 5);
    
              System.out.println(lru.get(0));
          }
    
          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;
    
              map = new LinkedHashMap<K, V>(capacity, hashLoadFactory, true) {
    
                  private static final long serialVersionUID = 1;
    
                  @Override
                  protected boolean removeEldestEntry(Map.Entry eldest) {
    
                      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();
          }
      }
    

猜你喜欢

转载自blog.csdn.net/captxb/article/details/91354822