美文网首页
LruCache源码分析

LruCache源码分析

作者: 鲁班0号 | 来源:发表于2019-05-12 23:17 被阅读0次

    LruCache代码只有300多行,看起来很简单

    0. 属性

    //主要的存储成员变量
    private final LinkedHashMap<K, V> map;
    //当前容量
    private int size;
    //最大的值
    private int maxSize;
    //put的次数 
    private int putCount;
    //create的次数 
    private int createCount;
    //回收次数
    private int evictionCount;
    //命中次数
    private int hitCount;
    //丢失的次数
    private int missCount;
    

    关于LinkedHashMap,我们已经讲过了,这里就不再叙述了

    1. 方法

    public LruCache(int maxSize) {
            if (maxSize <= 0) {
                throw new IllegalArgumentException("maxSize <= 0");
            }
            this.maxSize = maxSize;
            this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
        }
    
    • initialCapacity 用于初始化该 LinkedHashMap 的大小。
    • loadFactor(负载因子)这个LinkedHashMap的父类 HashMap 里的构造参数,涉及到扩容问题,比如 HashMap 的最大容量是100,那么这里设置0.75f的话,到75的时候就会扩容。
    • accessOrder,这个参数是排序模式,true表示在访问的时候进行排序( LruCache 核心工作原理就在此),false表示在插入的时才排序。

    1.1 put方法

     public final V put(K key, V value) {
            if (key == null || value == null) {
                throw new NullPointerException("key == null || value == null");
            }
            V previous;
            synchronized (this) {
                putCount++;
                //如果部重写,那么这个容量就是1
                size += safeSizeOf(key, value);
                previous = map.put(key, value);
                if (previous != null) {
                    //如果已经存在这个值了,那么就要删除容量
                    size -= safeSizeOf(key, previous);
                }
            }
            if (previous != null) {
               // 留给子类重写的方法
                entryRemoved(false, key, previous, value);
            }
            //调整缓存大小(关键方法)
            trimToSize(maxSize);
            return previous;
        }
    

    1.2 trimToSize方法

    public void trimToSize(int maxSize) {
            while (true) {
                K key;
                V value;
                synchronized (this) {
                    if (size < 0 || (map.isEmpty() && size != 0)) {
                        throw new IllegalStateException(getClass().getName()
                                + ".sizeOf() is reporting inconsistent results!");
                    }
    
                    if (size <= maxSize) {
                        break;
                    }
    
                    Map.Entry<K, V> toEvict = map.eldest();
                    if (toEvict == null) {
                        break;
                    }
    
                    key = toEvict.getKey();
                    value = toEvict.getValue();
                    map.remove(key);
                    size -= safeSizeOf(key, value);
                    evictionCount++;
                }
    
                entryRemoved(true, key, value, null);
            }
        }
    

    该方法就是循环查找如果当前的大小超过最大的值,那么就删除最旧的节点,回收次数+1,直到size小于maxSize为止。

    1.3 get()方法

    public final V get(K key) {
            if (key == null) {
                throw new NullPointerException("key == null");
            }
            V mapValue;
            synchronized (this) {
                mapValue = map.get(key);
                if (mapValue != null) {
                    hitCount++;
                    return mapValue;
                }
                missCount++;
            }
            V createdValue = create(key);
            if (createdValue == null) {
                return null;
            }
            synchronized (this) {
                createCount++;
                mapValue = map.put(key, createdValue);
    
                if (mapValue != null) {
                    // There was a conflict so undo that last put
                    map.put(key, mapValue);
                } else {
                    size += safeSizeOf(key, createdValue);
                }
            }
    
            if (mapValue != null) {
                entryRemoved(false, key, createdValue, mapValue);
                return mapValue;
            } else {
                trimToSize(maxSize);
                return createdValue;
            }
        }
    

    get方法流程是:

    1. 根据key从map中拿数据,如果拿到数据那么就命中次数+1,然后退出;
    2. 如果没有拿到map中数据,那么miss+1,然后创建value,LruCache中数据是null,所以会直接返回null;
    3. 如果子类重写了create的方法,那么createCount++,并且以这个key保存到map中,如果map中存在了key,那么重新put上一次的value,如果不为空,那么size变大。

    LruCahe先分析到这里!

    相关文章

      网友评论

          本文标题:LruCache源码分析

          本文链接:https://www.haomeiwen.com/subject/navcoqtx.html