jdk源码分析之LinkedHashMap

原创 2016年06月02日 11:11:06

基本原理

LinkedHashMap继承自HashMap,因此具有HashMap的所有特性。在HashMap的基础上,保持了key的插入顺序或者访问顺序。实现方法就是用双向循环链表将所有的entry链接起来,读取数据的时候直接读取此双向循环链表的数据即可。

public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>
    /**
     * The head of the doubly linked list.
     */
    private transient Entry<K,V> header;

    /**
     * The iteration ordering method for this linked hash map: 
     * true for access-order,false for insertion-order.
     */
    private final boolean accessOrder;

header是双向循环链表的头结点,不存储数据,用于定位头尾节点
header.after指向链表的第一个节点
header.before指向链表的最后一个节点
accessOrder是一个标志位,true的话表示按照访问顺序访问链表,可据此构建LRU缓存,fasle的话表示按照插入顺序访问链表

双向循环链表的Entry节点

LinkedHashMap的Entry继承自HashMap的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;

因此LinkedHashMap的Entry节点具有三个指针域,next指针维护Hash桶中冲突key的链表,before和after维护双向循环链表
为了维护双向循环链表,Entry新增加了4个方法

删除节点remove

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

添加节点同样是指针操作,非常高效方便

记录访问recordAccess

        /**
         * 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;
            if (lm.accessOrder) {
                lm.modCount++;
                remove();
                addBefore(lm.header);
            }
        }

当LinkedHashMap的标志位accessOrder为true时,标志着要采用访问顺序访问链表。

 remove();
 addBefore(lm.header);

最新访问的节点先从原来的位置删除,然后重现添加到链表的末尾,这样最近最少访问的节点就被挪到了链表的前端。

LinkedHashMap添加数据

LinkedHashMap本质上也是一个HashMap,在HashMap的基础上添加所有entry构成双向循环链表的功能。因此LinkedHashMap并没有覆写HashMap的put方法,只是覆写了HashMap中put方法调用的addEntry方法。
先来回顾下HashMap的put方法够干了什么

    public V put(K key, V value) {
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key.hashCode());
        int i = indexFor(hash, table.length);
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }

        modCount++;
        addEntry(hash, key, value, i);
        return null;
    }

HashMap的put方法主要功能是计算对应key的桶的位置,遍历桶中链表,找到对应key的entry修改原数据。遍历链表结束没有找到对应key的entry则调用addEntry方法将新添加的键值对entry添加到桶中entry链表的头部。
而对于LinkedHashMap中的entry节点来说,要维持两个链表,一个是桶中的next指针域链接的hask冲突的key构成的entry单链表,第二个就是维护所有entry构成的双向循环链表
LinkedHashMap的addEntry方法如下所示

    void addEntry(int hash, K key, V value, int bucketIndex) {
        createEntry(hash, key, value, bucketIndex);

        // Remove eldest entry if instructed, else grow capacity if appropriate
        Entry<K,V> eldest = header.after;
        if (removeEldestEntry(eldest)) {
            removeEntryForKey(eldest.key);
        } else {
            if (size >= threshold)
                resize(2 * table.length);
        }
    }

先调用createEntry把新节点链接到两条链表上,然后判断是否要删除最近最久未访问的节点(也就是双向循环链表的第一个节点),要删除的话就不需要检查是否需要扩容了,都则要检查是否需要扩容。

    void createEntry(int hash, K key, V value, int bucketIndex) {
        HashMap.Entry<K,V> old = table[bucketIndex];
    Entry<K,V> e = new Entry<K,V>(hash, key, value, old);
        table[bucketIndex] = e;
        e.addBefore(header);
        size++;
    }

可见新节点被两条链表都链接上了
第一条是单项非循环链表,桶中hash值冲突得key构成的entry链表

        HashMap.Entry<K,V> old = table[bucketIndex];
    Entry<K,V> e = new Entry<K,V>(hash, key, value, old);
        table[bucketIndex] = e;

第二条是所有entry节点构成的双向循环链表

        e.addBefore(header);

containsValue方法直接在双向循环链表中查找值

    public boolean containsValue(Object value) {
        // Overridden to take advantage of faster iterator
        if (value==null) {
            for (Entry e = header.after; e != header; e = e.after)
                if (e.value==null)
                    return true;
        } else {
            for (Entry e = header.after; e != header; e = e.after)
                if (value.equals(e.value))
                    return true;
        }
        return false;
    }

根据传入参数value是否为null,对双向循环链表进行遍历查找

相关文章推荐

HTTP协议分析系列(六)------php+socket+cookie请求

以www.verycd.com为例 在火狐浏览器登录wuming88888888账号为发送方 在chrome浏览器登录wuming1990账号为接收方 分析发送方的表单 分析提交页源代码POS...

Netty保持心跳

Netty保持心跳 在我们使用Netty进行长连接的时候,需要考虑如何去保持心跳。保持心跳有两种方式:一种是每到一定时间服务端发送一条保持心跳的报文给客户端维持长连接,一种是客户端发送一条保持心跳的...

java源码解读之LinkedHashMap------jdk 1.7

前面分析了HashMap的实现,我们知道其底层数据存储是一个hash表(数组+单向链表)。接下来我们看一下另一个LinkedHashMap,它是HashMap的一个子类,他在HashMap的基础上维持...

JDK之LinkedHashMap源码解析

刚入java一年的萌新,对于简单的使用已毫不满足,最终为了一探究竟,翻开了JDK的源码,以下观点为自己的理解及看了多篇博客的总结,欢迎各位大神指出不对的地方,当然也欢迎和我一样刚学的同学,一起加油努力...

JDK源码学习(4)-java.util.HashMap、LinkedHashMap与TreeMap

介绍java的Map的实现原理和源码。先研究非线程安全的Map

【源码解析】JDK源码之LinkedHashMap

LinkedHashMap源码,基于jdk1.6.43

LinkedHashMap源码解析 给jdk写注释系列之jdk1.6容器(5)

前面分析了HashMap的实现,我们知道其底层数据存储是一个hash表(数组+单向链表)。接下来我们看一下另一个LinkedHashMap,它是HashMap的一个子类,他在HashMap的基础上维持...

史上最详细的LinkedHashMap详解--源码分析

史上最详细的LinkedHashMap详解–源码分析

LinkedHashMap源码分析与LRU实现

LinkedHashMap可认为是哈希表和链接列表综合实现,并允许使用null值和null键。LinkedHashMap实现与HashMap的不同之处在于,LinkedHashMap维护着一个运行于所...

java-LinkedHashMap和LinkedHashSet源码分析

上一篇文章中,详细说明了HashMap和HashSet的源码,从源码的角度可以看出两者存在深入的联系,推测而来,LinkedHashMap和LinkedHashSet必然也存在着深入的联系。经过一下分...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:jdk源码分析之LinkedHashMap
举报原因:
原因补充:

(最多只允许输入30个字)