LinkedHashMap源码浅析(三):LruCache浅解

LRU(Least Recently Used)最近最少使用,是Android提供的一个缓存类,内部则由LinkedHashMap实现

先来看LruCache中的属性:

size 当前所缓存的对象数量
maxSize 当前LruCache所能承受的容量
putCount 添加对象的次数
createCount 添加的次数
evictionCount
hitCount命中次数,根据key能够正确查到指定的value的次数
missCount 未命中次数,根据key未能查到指定的value的次数
map 是一个LinkedHashMap 存放对象,是LruCache的核心

构造器
    public LruCache(int maxSize) {
        if (maxSize <= 0) {
            throw new IllegalArgumentException("maxSize <= 0");
        }
        this.maxSize = maxSize;
        this.map = new LinkedHashMap(0, 0.75f, true);
    }

传入一个最大容量,并初始化map;

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++;
            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;
    }

可以看到这里面加了一个同步锁synchronized,我们来看看里面,首先putCount++,然后通过safeSizeOf计算传入对象的大小,并加入到size,然后再将传入的对象添加到集合map中,如果传入的key值在集合中有对应的元素,还需要对原始元素的大小进行计算,最后减去原始元素的值,同步块之外还有两个方法entryRemoved() trimToSize(),加上前面说的计算对象大小的方法safeSizeOf()我们一起来看下

    private int safeSizeOf(K key, V value) {
        int result = sizeOf(key, value);
        if (result < 0) {
            throw new IllegalStateException("Negative size: " + key + "=" + value);
        }
        return result;
    }
    protected int sizeOf(K key, V value) {
        return 1;
    }    

可以看到safeSizeOf()中具体计算元素大小的地方是sizeOf(),而此方法默认返回为1,但是我们可以重写此方法,通过我们具体的业务来进行具体的元素类型大小的计算,
LinkedHashMap#entryRemoved():

    protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {}

默认是个空方法,但是在网上找了些介绍文字:

put发生key冲突时被调用,evicted = false,key为传入的key值,oldValue为该key所对应的原始value值,newValue为传入的value值
trimToSize的时候会被调用一次,就是最后一次被删除的最少访问数据带回来,evicted = true,newVlue = null
remove的时候,存在对应的key,并且被成功删除后调用,evicted = false, newVlue = null
再者这个方法的应用场景,我没有找到,虽然返回了原始值,但是原始值也可能被提前回收了,

LruCache#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 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);
        }
    }

整体是一个死循环,结束循环的条件是

当前集合中的元素总大小 < 设置集合的总容量
map.eldest() 返回当前集合的头结点,如果头结点为空,即集合为空,直接返回循环

综上可以看出此方法的作用是:当集合的元素的总大小大于指定的元素大小使,用来删除一些元素来空出一部分空间的操作。而删除的节点是从头结点开始的,符合访问顺序的特性,删除最近没有被访问的节点

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;
        }
    }

前一部分是正常的从集合中遍历指定的key值,如果存在指定的key值,hitCount++,并返回找到的key值,如果没有找到则missCount++,并且通过create()创建一个value值,如果这个值不为空则createValue++并将此key以及value放入到集合中。
create()是一个空方法,我们可以自己去继承实现

        LruCache lruCache = new LruCache(50){

            //自己实现的计算元素大小的方法
            @Override
            protected int sizeOf(String key, Bitmap value) {
                return super.sizeOf(key, value);
            }
        
            // 重新定义集合的最大容量
            @Override
            public void resize(int maxSize) {
                super.resize(maxSize);
            }
            // 如果没有找到此key对应的value,通过此方法可以创建一个value值
            @Override
            protected Bitmap create(String key) {
                return super.create(key);
            }
        };

你可能感兴趣的:(LinkedHashMap源码浅析(三):LruCache浅解)