1、简介
LinkedHashMap 继承自 HashMap,在 HashMap 基础上,内部维护了一个双向链表,
解决HashMap不能随时保持遍历顺序和插入顺序一致的问题。除此之外,LinkedHashMap 对访问顺序也提供了相关支持,可以用来实现LRU缓存策略。LinkedHashMap的实现很多方法直接继承自 HashMap,仅为维护双向链表覆写了部分方法。
2、继承关系
LinkedHashMap继承HashMap,拥有HashMap的所有特性,并且额外增加的按一定顺序访问的特性。LinkedHashMap 在HashMap结构的基础上,增加了一条双向链表,使得上面的结构可以保持键值对的插入顺序。同时通过对链表进行相应的操作,实现了访问顺序相关逻辑。其结构可能如下图:


3、源码分析
3.1 属性
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则按访问顺序存储元素
final boolean accessOrder;
3.2 构造方法
// 调用父类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);
}
3.3 HashMap三个回调方法
// HashMap
void afterNodeAccess(Node<K,V> p) { }
void afterNodeInsertion(boolean evict) { }
void afterNodeRemoval(Node<K,V> p) { }
// LinkedHashMap
// HashMap在putVal()插入后调用,默认值为true
void afterNodeInsertion(boolean evict) {
LinkedHashMap.Entry<K,V> first;
if (evict && (first = head) != null && removeEldestEntry(first)) {
K key = first.key;
removeNode(hash(key), key, null, false, true);
}
}
// 移除最近最少被访问条件之一,通过覆盖此方法可实现LRU 缓存
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
return false;
}
// 调用这个方法把访问到的节点移动到双向链表的末尾
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 接在链表的最后
p.before = last;
last.after = p;
}
tail = p;
++modCount;
}
}
// 在节点被删除之后调用的方法
// 把节点从双向链表中删除的方法
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、总结
(1)LinkedHashMap继承自HashMap,具有HashMap的所有特性;
(2)LinkedHashMap内部维护了一个双向链表存储所有的元素;
(3)LinkedHashMap可以用来实现LRU缓存淘汰策略;
- 例:
class LRU<K, V> extends LinkedHashMap<K, V> {
// 保存缓存的容量
private int capacity;
public LRU(int capacity, float loadFactor) {
super(capacity, loadFactor, true);
this.capacity = capacity;
}
/**
* 重写removeEldestEntry()方法设置何时移除旧元素
* @param eldest
* @return
*/
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
// 当元素个数大于了缓存的容量, 就移除元素
return size() > this.capacity;
}
}
本文详细介绍了LinkedHashMap的数据结构和实现原理,包括其如何在HashMap的基础上通过双向链表保持元素的插入或访问顺序,以及如何通过覆盖removeEldestEntry方法实现LRU缓存策略。
626

被折叠的 条评论
为什么被折叠?



