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;
          }
        };
      }
  • 相关阅读:
    SDL 开发实战(三):使用 SDL 绘制基本图形
    SDL 开发实战(二):SDL 2.0 核心 API 解析
    SDL 开发实战(一):SDL介绍及开发环境配置
    开源播放器 ijkplayer (六) :Android 下使用 ijkplayer 异常处理思路
    音视频编解码技术(二):AAC 音频编码技术
    FFmpeg开发实战(五):FFmpeg 抽取音视频的视频数据
    FFmpeg开发实战(四):FFmpeg 抽取音视频的音频数据
    FFmpeg开发实战(三):FFmpeg 打印音视频Meta信息
    FFmpeg开发实战(二):FFmpeg 文件操作
    FFmpeg开发实战(一):FFmpeg 打印日志
  • 原文地址:https://www.cnblogs.com/jiaoyiping/p/10604463.html
Copyright © 2011-2022 走看看