LinkedHashMap及其应用LruCache

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/fengluoye2012/article/details/82260438

LinkedHashMap

简介

LinkedHashMap是HashMap的子类;在HashMap的基础上又用双向链表来实现的;换句话说:LinkedHashMap的所有元素不仅满足HashMap的数据结构,同时满足LinkedList的结构,维护着这两套实现方式;

LinkedHashMap类主要是维护双向链表,其他的工作主要还是在HashMap中实现;

LinkedHashMapEntry

 static class LinkedHashMapEntry<K,V> extends HashMap.Node<K,V> {
    //这两个引用用来实现双向链表
    LinkedHashMapEntry<K,V> before, after;
    LinkedHashMapEntry(int hash, K key, V value, Node<K,V> next) {
        super(hash, key, value, next);
    }
}

主要变量

transient LinkedHashMapEntry<K,V> head;头结点,双链表中存在最久的节点;
transient LinkedHashMapEntry<K,V> tail;尾结点,链表中刚加入的节点;
final boolean accessOrder;是否根据访问顺序,改变双向链表的顺序;默认为false;实现LruCache算法的主要实现;

LinkedHashMap主要实现HashMap的三个空方法

 // Callbacks to allow LinkedHashMap post-actions
void afterNodeAccess(Node<K,V> p) { }
void afterNodeInsertion(boolean evict) { }
void afterNodeRemoval(Node<K,V> p) { }

put()方法

put()方法,还是调用HashMap的put()方法,主要区别在于putVal()中newNode()方法,会调用LinkedHashMap自己的newNode()方法;其他都一样,最后LikedHashMap方法实现了;

public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    if ((p = tab[i = (n - 1) & hash]) == null)
        //1 调用LinkedHashMap的newNode()创建节点;
        tab[i] = newNode(hash, key, value, null);
    else {
        Node<K,V> e; K k;
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;
        else if (p instanceof TreeNode)
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        else {
            for (int binCount = 0; ; ++binCount) {
                if ((e = p.next) == null) {
                    p.next = newNode(hash, key, value, null);
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        treeifyBin(tab, hash);
                    break;
                }
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;
            }
        }
        if (e != null) { // existing mapping for key
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            //2  如果key已存在,afterNodeAccess()在LinkedHashMap有具体的实现;
            afterNodeAccess(e);
            return oldValue;
        }
    }
    ++modCount;
    if (++size > threshold)
        resize();
    //3 不存在的话,HashMap插入新节点,LinkedHashMap有具体的实现;
    afterNodeInsertion(evict);
    return null;
}

LinkedHashMap的newNode()方法;

//创建新节点,插入双链表尾后,并返回该节点;
Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
    LinkedHashMapEntry<K,V> p =
        new LinkedHashMapEntry<K,V>(hash, key, value, e);
    linkNodeLast(p);
    return p;
}

//在链表尾插入新节点;
private void linkNodeLast(LinkedHashMapEntry<K,V> p) {
    LinkedHashMapEntry<K,V> last = tail;
    //将尾结点重新赋值;
    tail = p;
    //原尾结点为null,说明LinkedHashMap是空的,head也是null,将head也赋值;
    if (last == null)
        head = p;
    //head节点不为null,相互引用:节点p的before引用指向last节点,last节点的after引用指向p节点;
    else {
        p.before = last;
        last.after = p;
    }
}

afterNodeAccess():如果根据访问顺序排序,并且访问的节点不是尾结点;将当前节点从链表中删除,再插入到尾结点后;

 void afterNodeAccess(Node<K,V> e) { // move node to last
    LinkedHashMapEntry<K,V> last;
    //如果根据访问顺序改变双向链表的顺序,并且访问的节点不是尾结点;
    if (accessOrder && (last = tail) != e) {
        //强转e得到节点p,并得到该节点的前一个节点b,后一个节点a;
        LinkedHashMapEntry<K,V> p =
            (LinkedHashMapEntry<K,V>)e, b = p.before, a = p.after;
        //将节点p的after引用制null;
        p.after = null;
        //如果b为null,说明访问的节点是头结点,重新为头结点赋值;不是null,将b节点的after引用指向a节点;
        if (b == null)
            head = a;
        else
            b.after = a;

        //正常情况下,由于判断过当前节点p不是尾结点,a就不会是null,将a的before引用指向b;
        if (a != null)
            a.before = b;
        else
            last = b;

        //正常情况下,last节点不是null,将节点p添加到链表尾,last节点和p节点相互指向,p节点的before指向last节点,last节点的after引用指向p节点;
        if (last == null)
            head = p;
        else {
            p.before = last;
            last.after = p;
        }
        //为尾结点重新赋值;
        tail = p;
        ++modCount;
    }
}

afterNodeInsertion():删除双向链表中最老的节点即头结点;默认情况下removeEldestEntry()返回false;LruCache类会重写,返回true;

void afterNodeInsertion(boolean evict) { // possibly remove eldest
    LinkedHashMapEntry<K,V> first;
    //有put()方法可知evict为true;
    if (evict && (first = head) != null && removeEldestEntry(first)) {
        K key = first.key;
        //调用HashMap的removeNode()
        removeNode(hash(key), key, null, false, true);
    }
}

get()方法

get():获取对应的值,并且如果是根据访问排序,在双向链表中先删除当前节点e,再插入到链表尾节点后;改变双向链表的顺序;

 public V get(Object key) {
    Node<K,V> e;
    //调用HashMap的getNode()查找对应的节点,判读是否为null;不为null,
    if ((e = getNode(hash(key), key)) == null)
        return null;
    //根据访问排序的话,在双向链表中先删除当前节点e,再插入到链表尾节点后;
    if (accessOrder)
        afterNodeAccess(e);
    return e.value;
}

remove()方法

remove():完全调用HashMap的remove()方法,在删除之后调用afterNodeRemoval(),将当前节点从双向链表中删除;

void afterNodeRemoval(Node<K,V> e) { // unlink
    LinkedHashMapEntry<K,V> p =
        (LinkedHashMapEntry<K,V>)e, b = p.before, a = p.after;
    p.before = p.after = null;
    if (b == null)
        head = a;
    else
        b.after = a;
    if (a == null)
        tail = b;
    else
        a.before = b;
}

LruCache

内部使用LinkedHashMap作为成员变量存储元素;key,value都不能为null;根据LinkedHashMap的双链表和是否根据访问顺序排序特性来实现的;主要用于图片加载框架根据最近最少使用算法,删除不常用的图片,节约空间;

初始化

LruCache<String, String> lruCache = new LruCache<String, String>(10) {
        //默认返回1;如果是Bitmap 得到对应的byte;
        @Override
        protected int sizeOf(String key, String value) {
            //super.sizeOf(key,value);
            return 2;
        }

        //值被替换或者被删除
        @Override
        protected void entryRemoved(boolean evicted, String key, String oldValue, String newValue) {
            super.entryRemoved(evicted, key, oldValue, newValue);
        }
    };

put()方法:不存在key对应的节点,创建新的节点,加在双链表链尾;存在的话,将值替换;

 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++;
        //获取value的size,最后会调用sizeOf()方法,获取value的int值;并增加对应大小;
        size += safeSizeOf(key, value);
        //插入,如果存在key对应的节点,返回对应的值,否则返回null;
        previous = map.put(key, value);
        //如果不为null,减去对应的大小
        if (previous != null) {
            size -= safeSizeOf(key, previous);
        }
    }
    //entryRemoved 为空方法,重写,手动释放对象value;
    if (previous != null) {
        entryRemoved(false, key, previous, value);
    }
    //计算当前最大size
    trimToSize(maxSize);
    return previous;
}

trimToSize()方法:put()方法之后,判断当前size是否大于maxSize,如果大于,删除最近最少使用的元素(即从双链表头删除头结点)

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

get()方法:存在key对应的节点,直接返回对应的value;不存在,调用create(key)方法,该方法默认情况下,返回null;也可以创协create()方法,自己实现;

 public final V get(K key) {
    if (key == null) {
        throw new NullPointerException("key == null");
    }

    V mapValue;
    synchronized (this) {
        //根据key获取对应的value;
        mapValue = map.get(key);
        if (mapValue != null) {
            hitCount++;
            return mapValue;
        }
        missCount++;
    }

    /*
     * Attempt to create a value. This may take a long time, and the map
     * may be different when create() returns. If a conflicting value was
     * added to the map while create() was working, we leave that value in
     * the map and release the created value.
     */
    //create过程异步
    V createdValue = create(key);
    if (createdValue == null) {
        return null;
    }
    //create()返回的不为null
    synchronized (this) {
        createCount++;
        //插入,并返回oldVaue;
        mapValue = map.put(key, createdValue);
        //不为空,在create()之前,又插入了其他value,则以上一次的value为准
        if (mapValue != null) {
            // There was a conflict so undo that last put
            map.put(key, mapValue);
        //增加size
        } else {
            size += safeSizeOf(key, createdValue);
        }
    }

    if (mapValue != null) {
        entryRemoved(false, key, createdValue, mapValue);
        return mapValue;
    } else {
        trimToSize(maxSize);
        return createdValue;
    }
}

remove():存在key对应的节点,删除,并减去size;

 public final V remove(K key) {
    if (key == null) {
        throw new NullPointerException("key == null");
    }

    V previous;
    synchronized (this) {
        previous = map.remove(key);
        if (previous != null) {
            size -= safeSizeOf(key, previous);
        }
    }

    if (previous != null) {
        entryRemoved(false, key, previous, null);
    }

    return previous;
}

所有的分析内容到此结束了。如有问题,请多指教,谢谢!

猜你喜欢

转载自blog.csdn.net/fengluoye2012/article/details/82260438