在上一章节,咱们深入浅出的分析了 HashMap,如果你已读过 HashMap 的讲解,一定能够想到本文将要讲解的 LinkedHashMap 其实也是一样的,LinkedHashMap 继承于 HashMap,不同的是 LinkedHashMap 插入的元素,可以按照插入的顺序读取!
01、摘要
在集合系列的第一章,咱们了解到,Map 的实现类有 HashMap、LinkedHashMap、TreeMap、IdentityHashMap、WeakHashMap、Hashtable、Properties 等等。
本文主要从数据结构和算法层面,探讨 LinkedHashMap 的实现。
02、简介
LinkedHashMap 可以认为是 HashMap+LinkedList,它既使用 HashMap 操作数据结构,又使用 LinkedList 维护插入元素的先后顺序,内部采用双向链表(doubly-linked list)的形式将所有元素( entry )连接起来。
LinkedHashMap 继承了 HashMap,允许放入 key 为 null 的元素,也允许插入 value 为 null 的元素。从名字上可以看出该容器是 LinkedList 和 HashMap 的混合体,也就是说它同时满足 HashMap 和 LinkedList 的某些特性,可将 LinkedHashMap 看作采用 Linkedlist 增强的 HashMap。
打开 LinkedHashMap 源码,可以看到主要三个核心属性:
1 |
|
LinkedHashMap 在初始化阶段,默认按插入顺序来遍历
1 |
|
LinkedHashMap 采用的 Hash 算法和 HashMap 相同,不同的是,它重新定义了数组中保存的元素 Entry,该 Entry 除了保存当前对象的引用外,还保存了其上一个元素 before 和下一个元素 after 的引用,从而在哈希表的基础上又构成了双向链接列表。
源码如下:
1 |
|
可以直观的看出,双向链表头部插入的数据为链表的入口,迭代器遍历方向是从链表的头部开始到链表尾部结束。
除了可以保迭代历顺序,这种结构还有一个好处:迭代 LinkedHashMap 时不需要像 HashMap 那样遍历整个 table,而只需要直接遍历 header 指向的双向链表即可,也就是说 LinkedHashMap 的迭代时间就只跟 entry 的个数相关,而跟 table 的大小无关。
03、常用方法介绍
3.1、get方法
get 方法根据指定的 key 值返回对应的 value。该方法跟 HashMap.get() 方法的流程几乎完全一样,默认按照插入顺序遍历。
1 |
|
如果accessOrder
为true
的话,会把访问过的元素放在链表后面,放置顺序是访问的顺序
1 |
|
测试用例:
1 |
|
输出结果:
1 |
|
3.2、put方法
put(K key, V value) 方法是将指定的 key, value 对添加到 map 里。该方法首先会调用 HashMap 的插入方法,同样对 map 做一次查找,看是否包含该元素,如果已经包含则直接返回,查找过程类似于 get() 方法;如果没有找到,将元素插入集合。
1 |
|
LinkedHashMap 中覆写的方法
1 |
|
3.3、remove方法
remove(Object key) 的作用是删除 key 值对应的 entry,该方法实现逻辑主要以 HashMap 为主,首先找到 key 值对应的 entry,然后删除该 entry(修改链表的相应引用),查找过程跟 get() 方法类似,最后会调用 LinkedHashMap 中覆写的方法,将其删除!
1 |
|
LinkedHashMap 中覆写的 afterNodeRemoval 方法
1 |
|
04、总结
LinkedHashMap 继承自 HashMap,所有大部分功能特性基本相同,二者唯一的区别是 LinkedHashMap 在 HashMap 的基础上,采用双向链表(doubly-linked list)的形式将所有 entry 连接起来,这样是为保证元素的迭代顺序跟插入顺序相同。
主体部分跟 HashMap 完全一样,多了 header 指向双向链表的头部,tail 指向双向链表的尾部,默认双向链表的迭代顺序就是 entry 的插入顺序。
05、参考
1、JDK1.7&JDK1.8 源码