LinkedHashMap源码分析

来源:互联网 发布:淘宝评价后不计分 编辑:程序博客网 时间:2024/05/19 20:56

LinkedHashMap继承了HashMap,在hashmap的哈希表的基础上维护了一个双向循环链表。是哈希表和链表两种数据结构的结合体。
LinkedHashMap添加了两个重要的属性:LinkedHashMap.Entry类型的header–双向链表的头;boolean类型的accessOrder,用于确定链表是以访问顺序排序还是以插入顺序排序。

LinkedHashMap中的内部类Entry类,继承自HashMap.Entry类,添加了指向链表中前后元素的两个引用。

构造器

LinkedHashMap使用父类构造器进行构造,然后添加了对accessOrder的初始化和对header的初始化工作。


LinkedHashMap在HashMap的操作的基础上添加了链表中元素的引用关系的维护操作,以及按需进行元素顺序的维护操作。

基础设施

LinkedHashMap中关于链表部分的操作都是围绕其内部类Entry来展开的。用remove()和addBefore()维护添加或删除链表元素时元素之间的引用关系;如果需要按访问顺序排列链表,则用recordAccess()和recordRemoval()来动态维护链表的元素顺序。

    /**     * LinkedHashMap entry.     */    private static class Entry<K,V> extends HashMap.Entry<K,V> {        // These fields comprise the doubly linked list used for iteration.        Entry<K,V> before, after;        Entry(int hash, K key, V value, HashMap.Entry<K,V> next) {            super(hash, key, value, next);        }        /**         * Removes this entry from the linked list.         */        private void remove() {            before.after = after;            after.before = before;        }        /**         * Inserts this entry before the specified existing entry in the list.         */        private void addBefore(Entry<K,V> existingEntry) {            after  = existingEntry;            before = existingEntry.before;            before.after = this;            after.before = this;        }        /**         * This method is invoked by the superclass whenever the value         * of a pre-existing entry is read by Map.get or modified by Map.set.         * If the enclosing Map is access-ordered, it moves the entry         * to the end of the list; otherwise, it does nothing.         */        void recordAccess(HashMap<K,V> m) {            LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;            //如果LinkedHashMap定义为访问顺序的链表,那么修改modCount,将该元素移至链表的尾端。如果是插入顺序,则什么也不做(不进行顺序的调整)            if (lm.accessOrder) {                lm.modCount++;                remove();                addBefore(lm.header);            }        }        void recordRemoval(HashMap<K,V> m) {            remove();        }    }

重写的方法

addEntry():

void addEntry(int hash, K key, V value, int bucketIndex) {        super.addEntry(hash, key, value, bucketIndex);        //添加了对用作缓存时移出链表最老元素的支持        // Remove eldest entry if instructed        Entry<K,V> eldest = header.after;        if (removeEldestEntry(eldest)) {           //移除最老元素(访问最少的元素)            removeEntryForKey(eldest.key);        }    }

createEntry():

void createEntry(int hash, K key, V value, int bucketIndex) {        HashMap.Entry<K,V> old = table[bucketIndex];        Entry<K,V> e = new Entry<>(hash, key, value, old);        table[bucketIndex] = e;        //维护链表的前后引用关系,将新增的Entry添加至链表尾部        e.addBefore(header);        size++;    }

对于父类的put方法,1、如果哈希表中有相同的key,那么会调用recordAccess方法;2、如果哈希表中没有,新增Entry的话,会调用重写过的createEntry()方法,在添加新元素的同时,维护链表元素引用关系。注意:createEntry()方法是同时满足访问顺序和插入顺序的。所以不需要额外的调用recordAccess方法。

get():

public V get(Object key) {        Entry<K,V> e = (Entry<K,V>)getEntry(key);        if (e == null)            return null;            //添加了顺序调整        e.recordAccess(this);        return e.value;    }

containsValue和transfer

因为有所有元素的链表,所以元素的遍历相比父类而言需要访问的地址少。(hashmap需要访问整个capacity,而linkedhashmap只需要访问size。)

总结

LinkedHashMap通过继承HashMap和重写其部分方法,并增加了Entry的属性,给哈希表里的元素附上了链表数据结构,赋予了hashmap链表的功能。

通过accessOrder属性值true,和新的Entry的recordAccess方法,加上对父类createEntry方法的重写,使linkedhashmap拥有了维护访问顺序的能力。其关键就是在每次访问(put(包括新增元素)/get)某个元素时,调整该元素的位置至链表尾端。所以该链表具有访问顺序排序。

如果accessOrder为false(默认值),则每次访问元素(包括新增元素)只是添加元素至链表尾端,不做其他操作,即为插入顺序。

访问顺序的一个典型应用就是LRU的实现。
另外通过对removeEldestEntry方法提供实现,可以将linkedhashmap作为缓存来实现。

private static final int MAX_ENTRIES = 100;protected boolean removeEldestEntry(Map.Entry eldest) {       return size() > MAX_ENTRIES; }

当调用addEntry()方法时,如果size超限,那么最老的entry将被移除。