LinkedHashMap源码分析

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将被移除。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值