概述
LinkedHashMap 是通过哈希表和链表实现的,它通过维护一个链表来保证对哈希表迭代时的有序性,而这个有序是指键值对插入的顺序。另外,当向哈希表中重复插入某个键的时候,不会影响到原来的有序性。也就是说,假设你插入的键的顺序为 1、 2、 3、 4,后来再次插入 2,迭代时的顺序还是 1、 2、3、 4,而不会因为后来插入的 2 变成 1、 3、 4、 2。(但可以改变它的规则,使它变成 1、 3、 4、 2)
LinkedHashMap 的实现主要分两部分,一部分是哈希表,另外一部分是链表。哈希表部分继承了HashMap,链表部分维护有序性。
- 声明
//继承了HashMap 实现了map接口
public class LinkedHashMap<K,V> extends HashMap<K,V>
implements Map<K,V>
链表节点
//LinkedHashMap 的链表节点继承了 HashMap 的节点,而且每个节点都包含了前指针和后指针,所以这里可以看出它是一个双向链表
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);
}
}
//头指针
transient LinkedHashMap.Entry<K,V> head;
//尾指针
transient LinkedHashMap.Entry<K,V> tail;
//默认为 false。当为 true 时,表示链表中键值对的顺序与每个键的插入顺序一致,也就是说重复插入键,也会更新顺序
//简单来说,为 false 时,就是上面所指的 1、 2、 3、 4 的情况;为 true 时,就是 1、 3、 4、 2 的情况
final boolean accessOrder;
3.方法
HashMap中有三个空方法,表示的是在访问、插入、删除某个节点之后,进行一些处理,在LinkedHashMap中有各自的实现,LinkedHashMap通过重写这三个方法来保证链表的插入、删除的有序性
// Callbacks to allow LinkedHashMap post-actions
void afterNodeAccess(Node<K,V> p) { }
void afterNodeInsertion(boolean evict) { }
void afterNodeRemoval(Node<K,V> p) { }
- afterNodeAccess 方法
核心:判断访问节点是否为链表的尾节点,是尾节点时不需要进行任何操作,否则将节点移至链表尾部
void afterNodeAccess(Node<K,V> e) { // move node to last
LinkedHashMap.Entry<K,V> last;
//当 accessOrder 的值为 true,(代表根据插入顺序进行更新),且 e 不是尾节点
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.after = b;
else
last = b;
if (last == null)
head = p;
else
p.befter = last;
last.after = p;
tail = p;
++ modCount;
}
}
- afterNodeInsertion方法
插入一个新节点时进行调用,会把链表的头节点删除,删除方式使用HashMap的removeNode方法
当在尾部插入新节点,以及访问某节点将其放置链表尾部,头节点最少访问被删除
其中需重写removeEldestEntry方法
void afterNodeInsertion(boolean evict) { // possibly remove eldest
LinkedHashMap.Entry<K,V> first;
if (evict && (first = head) != null && removeEldestEntry(first)) {
K key = first.key;
removeNode(hash(key), key, null, false, true);
}
}
- afterNodeRemoval方法
删除链表中的节点
void afterNodeRemoval(Node<K,V> e) { // unlink
LinkedHashMap.Entry<K,V> p =
(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
p.before = p.after = null;
if (b == null)
head = a;
else
b.after = a;
if (a == null)
tail = b;
else
a.before = b;
}
4.get方法
调用 HashMap 的getNode 方法来获取结果。
public V get(Object key) {
Node<K,V> e;
if ((e = getNode(hash(key),key)) == null)
return null;
if (accessOrder)
afterNodeAccess(e);
return e.value;
}
put方法和remove方法没有进行重写