Android进阶;LinkedHashMap和LruCache

前言

LinkedHashMap是LruCache的基础,可以认为,LinkedHashMap是不限容量的Lru,Lru是限制了容量的LinkedHashMap。

LinkedHashMap

LinkedHashMap扩展自HashMap,而且数据存储对象改为扩展自HashMapEntry的LinkedHashMapEntry,主要是增加了before和after,实现双向链表。
LinkedHashMap持有一个LinkedHashMapEntry类型的header,作为链表的表头。

所以LinkedHashMap既有HashMap的数组+链表的二维存储结构,又有链表的前后关系的查找结构。
所以,清空数据集合时,既要调用父类HashMap的数据清空,又要调用自身链表的前后关系清空(把header的before和after全都指向header自己)。

LinkedHashMap自带部分LRU功能,构造函数:

    public LinkedHashMap(int initialCapacity,
                         float loadFactor,
                         boolean accessOrder) {
        super(initialCapacity, loadFactor);
        this.accessOrder = accessOrder;
    }

其中,第三个参数就代表是否开启LRU,如果为true,在get时,会挪到header前面,注意,header是不变的

        void recordAccess(HashMap<K,V> m) {
            LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
            if (lm.accessOrder) {
                lm.modCount++;
                remove();//从当前链表节点挪走
                addBefore(lm.header);//添加为新的header
            }
        }

所以,多次访问后,链表的数据关系为:
-第一个访问--第二个访问--第三个访问--header--最久没有访问-(这是一个双向链表)
所以,header前面是最近访问的,header后面是最久没有访问的。

因为遍历地时候会从header开始:

    private abstract class LinkedHashIterator<T> implements Iterator<T> {
        LinkedHashMapEntry<K,V> nextEntry    = header.after;

所以,最前面取得的数据就是最久没有访问的数据。

LruCache

因为LinkedHashMap可以根据最近访问进行排序,所以LruCache使用LinkedHashMap来存储数据:

    public LruCache(int maxSize) {
        ...
        this.maxSize = maxSize;
        this.map = new LinkedHashMap<K, V>(0, 0.75f, true);//开启LinkedHashMap的访问排序
    }

LinkeHashMap是可以自动扩容的,所以在LruCache中需要限制数据集合的上限,主要方法就是在put时,挤走最久没有访问的数据:

    public final V put(K key, V value) {
        ...
        trimToSize(maxSize);//挤走最久没有访问的数据
        return previous;
    }

其实就是从header头开始遍历LinkedHashMap,依次从最久没有访问的数据开始清理:

    public void trimToSize(int maxSize) {
        while (true) {
            K key;
            V value;
            synchronized (this) {
                ...
                if (size <= maxSize) {
                    break;
                }
                Map.Entry<K, V> toEvict = map.eldest();
                if (toEvict == null) {
                    break;
                }
                ...
                map.remove(key);
                ...
            }

            entryRemoved(true, key, value, null);
        }
    }

end

猜你喜欢

转载自blog.csdn.net/feiyu1947/article/details/86613770