LinkedHashMap 继承了 HashMap 类。
HashMap 类的源码详细分析参考博客:HashMap源码分析
LinkedHashMap底层仍是数组和链表实现的。但是为什么可以在迭代时,返回和插入顺序相同的顺序呢。
因为它不是用的 HashMap 的单向链表,而是双向链表。通过维护这个双向链表来保证对哈希表迭代时的有序性,即LinkedHashMap 采用的 hash 算法和 HashMap 相同,但是它重新定义了数组中保存的元素 Entry,该 Entry 除了保存当前对象的引用外,还保存了其上一个元素 before 和下一个元素 after 的引用,从而在哈希表的基础上又构成了双向链接列表。
属性
// 双向链表的头节点
transient LinkedHashMap.Entry<K,V> head;
// 双向链表的尾节点
transient LinkedHashMap.Entry<K,V> tail;
// 迭代的顺序;true的话按照访问顺序迭代;false则按照插入顺序迭代
final boolean accessOrder;
节点类
节点类继承自 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);
}
}
构造函数
构造函数可以参考 HashMap 的构造函数分析
public LinkedHashMap(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
accessOrder = false;
}
public LinkedHashMap(int initialCapacity) {
super(initialCapacity);
accessOrder = false;
}
public LinkedHashMap() {
super();
accessOrder = false;
}
public LinkedHashMap(Map<? extends K, ? extends V> m) {
super();
accessOrder = false;
putMapEntries(m, false);
}
public LinkedHashMap(int initialCapacity,
float loadFactor,
boolean accessOrder) {
super(initialCapacity, loadFactor);
this.accessOrder = accessOrder;
}
关键方法
这里可以注意一下 HashMap 中的 put 方法,其中涉及到下边的两个方法。在 HashMap 中,以下的两个方法为空,就是要在 LinkedHashMap 中预留的方法。
afterNodeAccess
在访问顺序的LinkedHashMap中,afterNodeAccess 在节点被访问过后,将访问的元素移到链表的最后一个元素的位置:
// 方法主要是
void afterNodeAccess(Node<K,V> e) {
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;
}
}
afterNodeInsertion
在新节点插入后的回调,这里removeEldestEntry返回false,即默认不移除头结点即最老的节点。这里可以进行重写该方法,来实现一个简单的LRU(Least Recently Used)Cache,即超出一定缓存后,最老的节点要删掉。
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);
}
}
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
return false;
}