1. 概述
HashMap 提供的访问,是无序的。而在一些业务场景下,我们希望能够提供有序访问的 HashMap 。那么此时,我们就有两种选择:
TreeMap :按照 key 的顺序。
LinkedHashMap :按照 key 的插入和访问的顺序。
2,类图
3. 属性
可以看到 LinkedHashMap 实现了自定义的节点 Entry ,一个支持指向前后节点的 Node 子类。代码如下:
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);
}
}
before
属性,指向前一个节点。after
属性,指向后一个节点。- 通过
before
+after
属性,我们就可以形成一个以 Entry 为节点的链表
既然 LinkedHashMap 是 LinkedList + HashMap 的组合,那么必然就会有头尾节点两兄弟。所以属性如下:
transient LinkedHashMap.Entry<K,V> head;
/**
* The tail (youngest) of the doubly linked list. 越新的节点,放在越后面。所以尾节点,指向链表的结尾
*/
transient LinkedHashMap.Entry<K,V> tail;
/**
* The iteration ordering method for this linked hash map: <tt>true</tt>
* for access-order, <tt>false</tt> for insertion-order.
*
* @serial是否按照访问的顺序
*/
final boolean accessOrder;
head
+ tail
属性,形成 LinkedHashMap 的双向链表。而访问的顺序,就是 head => tail
的过程
accessOrder
属性,决定了 LinkedHashMap 的顺序
true
时,当 Entry 节点被访问时,放置到链表的结尾,被 tail
指向。
false
时,当 Entry 节点被添加时,放置到链表的结尾,被 tail
指向。如果插入的 key 对应的 Entry 节点已经存在,也会被放到结尾。
4,构造方法
LinkedHashMap 一共有 5 个构造方法,其中四个和 HashMap 相同,只是多初始化 accessOrder = false
。所以,默认使用插入顺序进行访问。
另外一个 LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder)
构造方法,允许自定义 accessOrder
属性。代码如下:
public LinkedHashMap(int initialCapacity,
float loadFactor,
boolean accessOrder) {
super(initialCapacity, loadFactor);
this.accessOrder = accessOrder;
}
5. 创建节点
插入 key-value 键值对时,例如说 #put(K key, V value)
方法,如果不存在对应的节点就会调用newNode方法创建节点。
因为 LinkedHashMap 自定义了 Entry 节点,所以必然需要重写该方法
Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
LinkedHashMap.Entry<K,V> p = //创建 Entry 节点
new LinkedHashMap.Entry<K,V>(hash, key, value, e);
linkNodeLast(p); //添加到结尾
return p; // 返回
}
调用 linkNodeLast(LinkedHashMap.Entry<K,V> p)
方法,添加到结尾。代码如下:
private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
LinkedHashMap.Entry<K,V> last = tail; // 记录原尾节点到 last 中
tail = p; // 设置 tail 指向 p ,变更新的尾节点
if (last == null)
head = p; // 如果原尾节点 last 为空,说明 head 也为空,所以 head 也指向 p
else { // last <=> p ,相互指向
p.before = last;
last.after = p;
}
}
6,转换成 Set/CollectionkeySet()
keySet()
方法,获得 key Set 。代码如下:
public Set<K> keySet() {
Set<K> ks = keySet; // 获得 keySet 缓存
if (ks == null) { // 如果不存在,则进行创建
ks = new LinkedKeySet(); // LinkedKeySet 是 LinkedHashMap 自定义的
keySet = ks;
}
return ks;
}
其中, LinkedKeySet 是 LinkedHashMap 自定义的 Set 实现类。代码如下:
final class LinkedKeySet extends AbstractSet<K> {
public final int size() { return size; }
public final void clear() { LinkedHashMap.this.clear(); }
public final Iterator<K> iterator() {
return new LinkedKeyIterator();
}
public final boolean contains(Object o) { return containsKey(o); }
public final boolean remove(Object key) {
return removeNode(hash(key), key, null, false, true) != null;
}
public final Spliterator<K> spliterator() {
return Spliterators.spliterator(this, Spliterator.SIZED |
Spliterator.ORDERED |
Spliterator.DISTINCT);
}
public final void forEach(Consumer<? super K> action) {
if (action == null)
throw new NullPointerException();
int mc = modCount;
for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after)
action.accept(e.key);
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
7. 清空clear()
clear()
方法,清空 LinkedHashMap 。代码如下:
public void clear() {
// 清空
super.clear();
// 标记 head 和 tail 为 null
head = tail = null;
}