HashMap 是无序的kv键值对容器,TreeMap 则是根据key进行排序的kv键值对容器,而LinkedHashMap一样也是一个有序的kv键值对容器,区别是其排序方式依据的是进入Map的前后顺序java
LinkedHashMap
继承自 HashMap
, 直接看其内部方法,并无覆盖HashMap
的增删查询接口,连tables数组也没有从新覆盖,因此数据结构基本没啥变化node
首先看继承体系以下编程
public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>
看到这里,有个地方比较有意思,HashMap 既然已经实现了Map接口,为何 LinkedHashMap
也要另外实现 Map
接口?数组
HashMap
类,必然是实现了Map
接口的从我的角度触发,这应该是一种编程习惯的问题数据结构
一样从put(k,v)
方法出发,经过查看新增一个kv对,数据是如何保存的来肯定数据存储结构,由于 LinkedHashMap
并无覆盖 put()
方法,因此能够肯定底层的存储结构一致,那么有序是如何保证的呢?学习
查看源码,发现新增了两个成员.net
/** * The head (eldest) of the doubly linked list. */ transient LinkedHashMap.Entry<K,V> head; /** * The tail (youngest) of the doubly linked list. */ transient LinkedHashMap.Entry<K,V> tail; static class Entry<K,V> extends HashMap.Node<K,V> { Entry<K,V> before, after; Entry(int hash, K key, V value, Node<K,V> next) { super(hash, key, value, next); } }
上面两个维护的是一个双向链表的头尾,这个链表根据插入Map的顺序来维护Node节点的,以此保证了顺序code
既然LinkedHashMap
在HashMap
的基础上维护了一个双向链表,那么这个链表的增删修改的逻辑是怎样的?对象
LinkedHashMap
扩展了 Entry类,新增了before, after
, 分别指向该节点在链表中的先后节点blog
新建立一个节点
Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) { LinkedHashMap.Entry<K,V> p = new LinkedHashMap.Entry<K,V>(hash, key, value, e); // 将新增的节点放在链表的最后面 linkNodeLast(p); return p; }
依然以put(k,v)
做为研究对象,分析链表的关系维护
主要方法: java.util.HashMap#putVal
当插入已经存在的kv对时,不会建立新的Node节点,而会调用下面的方法
void afterNodeAccess(Node<K,V> e) { // move node to last LinkedHashMap.Entry<K,V> last; if (accessOrder && (last = tail) != e) { LinkedHashMap.Entry<K,V> p = (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after; p.after = null; if (b == null) head = a; else b.after = a; if (a != null) a.before = b; else last = b; if (last == null) head = p; else { p.before = last; last.after = p; } tail = p; ++modCount; } }
accessOrder
true表示链表的顺序根据访问顺序来,false表示根据插入顺序来
默认会设置为false,此时上面的逻辑基本上不走到,即表示插入一个已存在的kv对,不会修改链表的顺序
若是显示设置 accessOrder
为true,则会将修改的节点,放在链表的最后
新增一个不存在的kv对,首先是调用上面的方法,建立一个Node节点: LinkedHashMap.Entry<K,V>
, 在建立节点的同时,就已经将节点放在了链表的最后, 实现逻辑以下
// link at the end of list private void linkNodeLast(LinkedHashMap.Entry<K,V> p) { LinkedHashMap.Entry<K,V> last = tail; tail = p; if (last == null) head = p; else { p.before = last; last.after = p; } }
同上
LinkedHashMap
存储结构和 HashMap
相同,依然是数组+链表+红黑树LinkedHashMap
额外持有一个双向链表,维护插入节点的顺序扫一扫二维码,关注小灰灰blog