LinkedHashMap继承自HashMap,拥有HashMap的所有特性,再此基础上,还提供两大特性: 1、按照插入顺序进行访问 2、实现访问最少最先删除,相当于LRU淘汰策略
属性
/**
* 继承hashmap的Node类,为每个元素增加类before和after属性
*/
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);
}
}
private static final long serialVersionUID = 3801124242820219131L;
/**
* 链表头
*/
transient LinkedHashMap.Entry<K,V> head;
/**
* 链表尾
*/
transient LinkedHashMap.Entry<K,V> tail;
/**
* 两种访问方式,默认false
* false:按照插入顺序提供访问
* true:按照访问顺序,把经常访问的值放到队尾
*/
final boolean accessOrder;
总体来说,LinkedHashMap相当于HashMap和LinkedList结合体,把Map的每个节点串联起来,保证不同元素的顺序性
按照插入顺序访问
按照顺序新增
//新建节点,并加到链表的尾部
TreeNode<K,V> newTreeNode(int hash, K key, V value, Node<K,V> next) {
TreeNode<K,V> p = new TreeNode<K,V>(hash, key, value, next);
linkNodeLast(p);
return p;
}
private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
LinkedHashMap.Entry<K,V> last = tail;
tail = p;
if (last == null)//链表为空
head = p;
else {//链表不为空
p.before = last;
last.after = p;
}
}
按照顺序访问
LinkedHashMaP只提供单向访问,可以通过迭代器进行访问。可以对key、value、entity提供迭代方法。
以LinkedHashIterator为例,其代码如下:
abstract class LinkedHashIterator {
LinkedHashMap.Entry<K,V> next;
LinkedHashMap.Entry<K,V> current;
int expectedModCount;
LinkedHashIterator() {
next = head;//头节点作为第一个访问的节点
expectedModCount = modCount;
current = null;
}
public final boolean hasNext() {
return next != null;
}
final LinkedHashMap.Entry<K,V> nextNode() {
LinkedHashMap.Entry<K,V> e = next;
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
if (e == null)//链表为空
throw new NoSuchElementException();
current = e;
next = e.after;//找到下一个节点
return e;//返回next节点,第一个返回的是头节点
}
public final void remove() {
Node<K,V> p = current;
if (p == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
current = null;
K key = p.key;
removeNode(hash(key), key, null, false, false);
expectedModCount = modCount;
}
}
访问最少删除策略(LRU)
这种策略的意思就是经常访问的元素会被追加到队尾,这样不经常访问的就在对头,可以通过设置删除策略,删除不经常访问的节点
访问方法
public V get(Object key) {
Node<K,V> e;
if ((e = getNode(hash(key), key)) == null)
return null;
if (accessOrder)//策略是LRU
afterNodeAccess(e);//将这个节点移动到队尾
return e.value;
}
void afterNodeAccess(Node<K,V> e) { // move node to last
LinkedHashMap.Entry<K,V> last;
if (accessOrder && (last = tail) != e) {//首先保证访问策略是LRU
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;
}
}
删除策略
LinkedHashMap本身没有put方法,调用的是HashMap的put方法(继承hashmap类),但LinkedHashMap实习了put方法中的调用afterNodeInsertion方法,这个方法实现了删除
void afterNodeInsertion(boolean evict) { // possibly remove eldest
LinkedHashMap.Entry<K,V> first;
//removeEldestEntry()来控制删除策略
if (evict && (first = head) != null && removeEldestEntry(first)) {
K key = first.key;
//删除头节点
removeNode(hash(key), key, null, false, true);
}
}