Java集合框架—LinkedHashMap—源码研读

Java集合框架—LinkedHashMap—源码研读_第1张图片
3.jpg

在上一篇文章:Java集合框架—HashMap—源码研读-2 中,我们讲解的是Map接口下的HashMap,今天让我们来看看另一个Map实现类:LinkedHashMap

本篇文章内容主要分为以下几点:

1.Map接口实现类的构成和关系图。

2.HashMap和LinkedHashMap的关系梳理

3.LinkedHashMap的底层数据结构

4.LinkedHashMap源码解读


1.Map接口实现类的构成和关系图

在说Map之前,让我们梳理一下Java集合框架的结构:

在Java集合框架中,分为两大类别:

1.实现了Collection接口的类—【单列集合】

2.实现了Map接口的类—【双列集合】

为什么区分为单列和双列?其实很好理解,Collection接口下又包含三大主接口:List、Queue、Set,实现了这三大接口的类,里面存放的都是单个元素,所以称为单列集合。而实现Map接口的类,存放的全是【key】-【value】类型的键值对,总是成双出现,故称为双列集合

让我们看一张比较完整的图:

Java集合框架—LinkedHashMap—源码研读_第2张图片
image

这张图乍一看可能会有点蒙,没关系,等一点点学完这些再看就OK了。

现在,让我们回到今天的主题:Map接口,来看一下Map接口下有哪些实现类?

Java集合框架—LinkedHashMap—源码研读_第3张图片
image

可以看到Map接口下又分为SortedMap接口、AbstractMap接口。

实现SortedMap接口的实现类有TreeMap。

实现AbstractMap接口的实现类有:TreeMap、HashMap、ConcurrentHashMap。

而LinkedHashMap直接继承自HashMap。

2.HashMap和LinkedHashMap的关系梳理

看完上面Map接口的类图,大家应该对Map系列有了一个大概的印象,现在让我们回到LinkedHashMap:

Java集合框架—LinkedHashMap—源码研读_第4张图片
image

如图:LinkedHashMap直接继承了HashMap,属于其子类,且实现了Map接口。

是不是很简单?那么,问题来了,既然有HashMap了,那为什么还需要LinkedHashMap?

说到这里,就不得不从HashMap开始说起,HashMap虽然是一种高效的数据结构,既有数组随机访问的优势,又有链表能快速增删的特点,但它有个最大的缺点:不能记录key-value值插入的顺序,因为HashMap索引index是根据key通过hash(key)来随机映射的,所有并不能记录和保存元素的插入顺序,这就比较尴尬了。

为了解决这个问题,设计者们创造出了—HashMap的子类LinkedHashMap,其仍然保留HashMap的数据结构,只额外增加了双链表的设计来将插入的元素链接起来从而让其有序。这样保证了遍历LinkedHashMap时就可以根据元素的插入顺序(或访问顺序)输出元素。

3.LinkedHashMap的底层数据结构

在了解了LinkedHashMap和HashMap的关系后,让我们来看一下LinkedHashMap的底层数据结构,LinkedHashMap在HashMap增加了双链表的设计,那么究竟是怎么设计的?双链表又是如何添加到HashMap上的?

来看一张图:

Java集合框架—LinkedHashMap—源码研读_第5张图片
image

可以看到LinkedHashMap的底层数据结构,就是HashMap的链表数组,只不过对每一个原Node节点,额外增加了2个“指针”用于连接插入的节点。而添加了2个指针后的Node节点,在LinkedHashMap即为Entry。待会,让我们从源码来看一下LinkedHashMap中Entry的构成。

4.LinkedHashMap源码解读

Java集合框架—LinkedHashMap—源码研读_第6张图片
image

首先,类的定义:

public class LinkedHashMap extends HashMap implements Map

这个我们之前已经说过了,现在看一下LinkedHashMap中的成员变量吧:

boolean的成员变量:accessOrder

Entry节点类变量:head

Entry节点类变量:tail

1.在看这些成员变量之前,我们首先来看一下内部类Entry的定义:

    /**
     * HashMap.Node subclass for normal LinkedHashMap entries.
     */
    static class Entry extends HashMap.Node {
        Entry before, after;
        Entry(int hash, K key, V value, Node next) {
            super(hash, key, value, next);
        }
    }

可以看见Entry继承了HashMap中的Node,Entry类中有两个Entry类型的成员变量before和after,即我们上面所说的双链表中,用于链接前后节点的“指针”;然后Entry类的构造器方法中通过super调用了Node节点的构造方法。

总结下就是:LinkedHashMap中的节点Entry继承自HashMap中的节点Node,且Entry中添加了before和after用链接节点从而构成双链表。

2.回到LinkedHashMap中的成员变量:

boolean的成员变量:accessOrder

Entry节点类变量:head

Entry节点类变量:tail

现在就很清楚了,head和tail就是用于标记双链表头和尾的指针节点。accessOrder是干嘛的?让我们看下源码上的注释:

/*
** The iteration ordering method for this linked hash map: {*@code true}
** for access-order, {
@code *false} for insertion-order.

** @serial*
*/

翻译下:accessOrder用于标记此LinkedHashMap中遍历迭代的顺序

accessOrder = true时按访问顺序遍历,false时,按插入顺序遍历

3.解读LinkedHashMap的put()方法源码:

虽然,我们已经清楚地知道了LinkedHashMap双链表的底层实现—Entry节点,但对于插入过程,节点之间怎么链接的还不太清楚,让我们从put()方法入手,看一下元素的插入和链接过程吧......

在源码中找了一圈,并没有发现put()方法,哈哈此处有坑!LinkedHashMap继承自HashMap,且并没有重写put()方法,LinkedHashMap添加元素用的就是HashMap中的put()方法。那么,问题又来了,如果没有重写put方法,那么添加元素的时候是如何记录顺序,链接节点的呢?

我们回到HashMap中putVal方法中,发现有个newNode()方法,每次添加新节点时总会通过newNode(hash, key, value, null);来生成一个新节点,然后再插入。哦,原来如此。

回到LinkedHashMap的源码中,果然我们发现了这个newNode()方法:

Java集合框架—LinkedHashMap—源码研读_第7张图片
image

newNode()中new了一个Entry节点,然后调用linkNodeLast()方法将节点链接到链表尾。最后return了这个节点。现在,一切都清楚了:

一个LinkedHashMap对象调用put()方法添加对象时,调用的是其父类HashMap中的put方法,根据key,生成hash值再求出其在table中的索引i,然后放入相应桶中的过程完全一样。只是在new节点元素时将原Node节点变成了包含链表指针的Entry节点,然后多了移步操作:将此节点链接到双链表的表尾。


看完文章觉得不错亲盆好友们,麻烦高抬贵手,点个哦,谢谢~

下一篇文章:Java集合框架—TreeMap—源码研读

你可能感兴趣的:(Java集合框架—LinkedHashMap—源码研读)