LinkedHashMap那点事儿
是前面分析了 HashMap 源码,今天分析下 LinkedHashMap,废话不多说直接开始。
LinkedHashMap
众所周知,HashMap 里的元素是无序的。如果想按照一定的顺序,比如插入顺序来访问元素,就需要 LinkedHashMap 这个集合来实现了,LinkedHashMap 相较于 HashMap 最大的特点就是元素有序。
LinkedHashMap 的构造方法是基于 HashMap的,不同之处是多了 accessOrder 这一属性。
数据结构
LinkedHashMap 是数组+单向链表+双向链表+红黑树的结构(便于理解,没画红黑树,红黑树其实也是在双向链表里)。
图中表示,节点 A 和 B 和 D,E 和 C的 key 有 hash 冲突,节点顺序是 A->B->E->C->D
核心属性
1 | /** |
注:accessOrder是 LinkedHashMap 的排序策略,默认是false,即按照插入顺序排序,设置为 true 表示按照访问顺序来排序,可在初始化 LinkedHashMap 时指定。
1 | LinkedHashMap<Integer, String> map = new LinkedHashMap<>(5,0.75f,true); |
控制台输出:
1 | {1=A, 2=B, 4=D, 5=E, 3=C} |
哪个元素最后被访问,则会被放置到末尾节点。
Entry
LinkedHashMap 中的节点是静态内部类 Entry,它是 HashMap节点 Node的子类。
1 | static class Entry<K,V> extends HashMap.Node<K,V> { |
可以看出Entry 节点继承了 HashMap 的节点 Node,保留了 Node 的单向链表特点,但自身拥有 before 和 after 两个属性,同时 LinkedHashMap 继承自 HashMap,所以 LinkedHashMap 的数据结构是 数组+单向链表+双向链表。
继承关系图:
常用方法
链表添加节点
nowNode(…)
1 | Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) { |
put(…)
LinkedHashMap 的 put 方法实际是调用 HashMap 的 put() 方法(参照上篇文章 [HashMap那点事儿](link: http://zhengyk.cn/2018/07/14/java/HashMap/ ) ),不同的是在 HashMap 的 putVal() 方法中 afterNodeAccess()、afterNodeInsertion()方法是空方法,具体由 LinkedHashMap来实现,流程如下图:
- HashMap 的 putVal 方法中:
1 | if (e != null) { // put 的 key 已存在 |
当向 LinkedHashMap 中 put一个元素时,如果 key 已存在,除了覆盖 value外,LinkedHashMap 会调用afterNodeAccess() 方法,如果accessOrder 是 true,且被覆盖的元素节点不是 last 节点,那么需要移动该节点至双向链表末尾
- afterNodeAccess()
1 | void afterNodeAccess(Node<K,V> e) { |
- afterNodeInsertion()
1 | void afterNodeInsertion(boolean evict) { // possibly remove eldest |
获取元素
1 | public V get(Object key) { |
LinkedHashMap 重写了 get() 方法,多了一步判断,如果是按照访问顺序来排序的话,每一次 get 元素,都会使该元素所在的节点移动至链表末尾。
总结
LinkedHashMap 在 HashMap 的基础上拓展了双向链表,在分析 LinkedHashMap 的源码时要先理解它的数据结构模型,结合模型去拆解、分析源码就会很简单。
- Title: LinkedHashMap那点事儿
- Author: 薛定谔的汪
- Created at : 2018-07-26 18:01:54
- Updated at : 2023-11-17 19:37:37
- Link: https://www.zhengyk.cn/2018/07/26/java/LinkedHashMap/
- License: This work is licensed under CC BY-NC-SA 4.0.