简介
LinkedHashMap扩展了HashMap,是HashMap的二次封装,加入了一个单独链表存所有数据,并且完整保留HashMap结构。
LinkedHashMap类
public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>
继承HashMap,在HashMap原有结构基础上又维护了一个链表
重要内部类LinkedHashMap.Entry<K,V>
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中也用到,HashMap的TreeNode就是继承此类(是不是很绕)
LinkedHashMap属性
// 链表头结点
transient LinkedHashMap.Entry<K,V> head;
// 链表尾结点
transient LinkedHashMap.Entry<K,V> tail;
// LRU算法相关 false 基于插入顺序,true 基于访问顺序
final boolean accessOrder;
LinkedHashMap构造函数
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;
}
从构造函数中可以看出,LinkedHashMap几乎完全使用HashMap的构造方法初始化(参照HashMap类篇),至于初始长度和加载因子也是延用HashMap,LinkedHashMap类自身只关注链表结构,数字加链表结构交给父类去维护
根据键取值
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;
}
实际上就是调用HashMap的getNode方法, afterNodeAccess(e)方法只是为了维护顺序,除了这个方法LinkedHashMap没有添加删除方法,那么他是怎么维护链表结构的呢?是否还记得在HashMap篇有几个方法要放在LinkedHashMap讲
void afterNodeAccess(Node<K,V> p) { }
void afterNodeInsertion(boolean evict) { }
void afterNodeRemoval(Node<K,V> p) { }
这几个方法出现在HashMap元素增加或减少之后,就是为了给hashMap子类使用
父类元素变动后方法
// HashMap删除节点后
void afterNodeRemoval(Node<K,V> e) {
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;
}
HashMap删除元素成功后会调用此方法afterNodeRemoval
// 插入成功删除头节点
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);
}
}
afterNodeInsertion方法是在哈希表中插入了一个新节点时调用的,它会把链表的头节点删除掉,删除的方式是通过调用HashMap的removeNode方法。我们要使用此功能必须重写removeEldestEntry方法
// accessOrder为true时将节点移到最后
void afterNodeAccess(Node<K,V> e) { // move node to last
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;
}
}
当accessOrder设置为true时,把当前节点e移至链表的尾部,在HashMap的putVal方法中,会调用此方法