LinkedHashMap
- LinkedHashMap 继承自 HashMap 类,基本上没有重写 HashMap 的方法,只是把 HashMap 空实现的预留拓展点实现了。
数据结构
- Map 拓展属性
/**
* 头节点。
*
* 越老的节点,放在越前面。所以头节点,指向链表的开头
*/
transient LinkedHashMap.Entry<K,V> head;
/**
* 尾节点
*
* 越新的节点,放在越后面。所以尾节点,指向链表的结尾
*/
transient LinkedHashMap.Entry<K,V> tail;
/**
* 是否按照访问的顺序。
*
* true :按照 key-value 的插入和访问顺序进行排序(即放置到链表的结尾,被 tail 指向)。
* false :按照 key-value 的插入顺序进行排序,访问该节点不会给该节点重新排序,
* 如果插入的 key 对应的 Entry 节点已经存在,也会被放到结尾。
*/
final boolean accessOrder;
- 数据节点 Entry (拓展了前后指针)
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);
}
}
构造方法
LinkedHashMap 一共有 5 个构造方法,其中四个和 HashMap 相同,只是多初始化 accessOrder = false
。所以,默认使用插入顺序进行访问。
- 允许自定义
accessOrder
属性。
public LinkedHashMap(int initialCapacity,
float loadFactor,
boolean accessOrder) {
super(initialCapacity, loadFactor);
this.accessOrder = accessOrder;
}
创建节点
和 HashMap 的 newNode() 不同点是会让 tail 指向该节点,并且 Entry 是双向链表
Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
// <1> 创建 Entry 节点
LinkedHashMap.Entry<K,V> p =
new LinkedHashMap.Entry<>(hash, key, value, e);
// <2> 添加到结尾
linkNodeLast(p);
// 返回
return p;
}
// 添加到结尾,被 tail 指向
private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
// 记录原尾节点到 last 中
LinkedHashMap.Entry<K,V> last = tail;
// 设置 tail 指向 p ,变更新的尾节点
tail = p;
// 如果原尾节点 last 为空,说明 head 也为空,所以 head 也指向 p
if (last == null)
head = p;
// last <=> p ,相互指向
else {
p.before = last;
last.after = p;
}
}
后置拓展点
- afterNodeAccess()
在 accessOrder
属性为 true
时,当 Entry 节点被访问时,放置到链表的结尾,被 tail
指向。
void afterNodeAccess(Node<K,V> e) {
// move node to last
LinkedHashMap.Entry<K,V> last;
// accessOrder 判断必须是满足按访问顺序。
// (last = tail) != e 将 tail 赋值给 last,并且判断是否 e 已经是队尾。如果是队尾,就不处理了。
if (accessOrder && (last = tail) != e) {
// 将 e 赋值给 p 【因为要 Node 类型转换成 Entry 类型】
// 同时 b、a 分别是 e 的前后节点
LinkedHashMap.Entry<K,V> p =
(LinkedHashMap.Entry<K,V>)e, b