zoukankan      html  css  js  c++  java
  • 如何使用LinkedHashMap来实现一个LruCache

    最近在看mybatis的源代码,发现了mybatis中实现的LruCache使用到了LinkedHashMap,所以就探究了一下LinkedHashMap是如何支持Lru缓存的

    LinkedHashMap内部维护了一个所有的Entity的双向链表

    同时构造方法可以设置Iterator的时候,是按照插入的顺序排序还是按照访问的顺序排序

    默认是按照插入的顺序来排序的,在构造方法里边可以设置按照访问的顺序来排序

    那究竟按照访问的顺序来排序是什么意思呢?

    LinkedHashMap的get(key)方法是自己实现的,并没有从HashMap里边继承,我们看看get(Key)方法的实现是什么样子的

    我们看afterNodeAccess()方法是如何实现的

    这个方法主要就是移动双向链表的指针,将传入的结点移动到LinkedHashMap维护的双向链表的末尾,这样每次通过get(key)方法访问一个元素,这个元素就会被移动到双向链表的末尾,按照访问的顺序来排序,就是每次通过Iterator来遍历keySet或者是EntrySet的时候,访问过的元素会出现在最后边(因为LinedHashMap的Iterator遍历的时候,遍历的是内部的双向链表,从头结点,遍历到尾结点)

    顺着这样的思路,如果在满足一定条件的情况下,移除掉双向链表的头结点,这样就实现了一个LruCahe

    其实LinkedHashMap已经为我们提供了这样的方法,LinkedHashMap中有一个方法removeEldestEntry(entry) 我们只需要覆盖这个方法,根据我们自己的需求在一定条件下返回true,这样就实现了LruCache
    改方法的默认实现是返回false
    protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
    return false;
    }

    LinkedHashMap的afterNodeInsertion()方法会根据其他条件以及removeEldestEntry的返回值来决定是否删除到双向链表的表头元素

    依据此,我们使用LinkedHashMap来实现一个最简单的Lru缓存如下:
    import org.junit.Test;

    import java.util.LinkedHashMap;
    import java.util.Map;
    public class TestCache {
        @Test
        public void testLinkedHashMap() {
            LinkedHashMap<String, String> map = new LinkedHashMap<String, String>(5, 0.75F, true) {
                @Override
                protected boolean removeEldestEntry(Map.Entry<String, String> eldest) {
                    //当LinkHashMap的容量大于等于5的时候,再插入就移除旧的元素
                    return this.size() >= 5;
                }
            };
            map.put("aa", "bb");
            map.put("cc", "dd");
            map.put("ee", "ff");
            map.put("gg", "hh");
            print(map);
            map.get("cc");
            System.out.println("===================================");
            print(map);
    
            map.get("ee");
            map.get("aa");
            System.out.println("====================================");
            map.put("ss","oo");
            print(map);
        }
    
        void print(LinkedHashMap<String, String> source) {
            source.keySet().iterator().forEachRemaining(System.out::println);
        }
    }   
    

    Mybatis中的Lrucache实现也是类似的思路,比较简单,下边是关键的代码:

    构造方法中调用了setSize()方法,默认缓存1024个元素

    public LruCache(Cache delegate) {
        this.delegate = delegate;
        setSize(1024);
      }
    

    setSize()方法中初始化了HashMap,并实现了removeEldestEntry()方法

    public void setSize(final int size) {
        keyMap = new LinkedHashMap<Object, Object>(size, .75F, true) {
          private static final long serialVersionUID = 4267176411845948333L;
    
          @Override
          protected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {
            boolean tooBig = size() > size;
            if (tooBig) {
              eldestKey = eldest.getKey();
            }
            return tooBig;
          }
        };
      }
  • 相关阅读:
    Error: could not open `C:Program FilesJavajre6libi386jvm.cfg'
    异常:java.lang.IllegalStateException: Ambiguous handler methods mapped for HTTP path '/app/userInfoMaint/getProvince.do'
    解析Java反射
    Cause: java.sql.SQLException: 无效的列索引
    Internet Explorer 无法打开该 Internet 站点,请求的站点不可用或无法找到
    解决The JSP specification requires that an attribute name is preceded by whitespace问题
    pl/sql的to_char和to_date
    oracle 在xml中批量插入,批量修改及多组条件查询
    时间转换模板
    Java网络编程从入门到精通(5):使用InetAddress类的getHostName方法获得域名
  • 原文地址:https://www.cnblogs.com/jiaoyiping/p/10604463.html
Copyright © 2011-2022 走看看