一、概述
- LinkedHashMap继承了HashMap,同时具有可预测的跌打顺序,按照插入的顺序排序。
- 与HashMap的不同之处在于,维护了一条贯穿其全部Entry的双向链表,由双向链表,维护整个map中entry的插入顺序,也正是因为维护双向链表,性能上不如HashMap,集合视图的遍历与元素个数成正比,而HashMap是与buckets数组的长度呈正比的。
- 可以理解为LinkedHashMap是散列表和链表的结合。
二、用途
- 如果需要维护map中的元素插入顺序,可以使用LinekedHashMap。
- 简单的LRU缓存列表。
三、源码分析
1.内部类
entry继承自HashMap.Node,不过增加了两个指针,指向前一个、后一个元素(双向链表)。
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);
}
}
2.成员变量
// 双向链表的头指针
transient LinkedHashMap.Entry<K,V> head;
// 双向链表的尾指针
transient LinkedHashMap.Entry<K,V> tail;
/**
* 迭代顺序模式的标记位,如果为true,采用访问排序,否则,采用插入顺序
* 默认插入顺序(构造函数中默认设置为false)
*/
final boolean accessOrder;
3.构造函数
构造函数中大部分默认accessOrder = false,使用的是插入顺序。当然也提供了一个构造函数,设置迭代顺序,当为true时,迭代时使用访问顺序,即按照最近访问的entry的次数从小到大排序,从最近最少访问到最近最多访问,基于这点可以简单实现一个LRU策略的缓存。
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;
}
4.查询操作
可以看出,accessOrder为true时,查询到Node时,会有一个将当前Node放入到双向链表末尾的操作,其实也就是说链表最后一个也不一定是最近访问最多的,但一定是最近访问的。
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;
}
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;
}
}
5.删除操作
LinkedHashMap使用的remove操作其实是HashMap中的remove操作,只是实现了afterNodeRemoval函数,用于在删除Node后,维护链表的顺序,而HashMap中的该方法为空方法,就是用于LinkedHashMap实现的。
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;
}
6.新增操作
与删除操作类似,LinkedHashMap重写了afterNodeInsertion函数,用于在新增后,维护链表顺序。
// evict是一个模式标记,如果为false代表buckets数组处于创建模式
// HashMap.put()函数对此标记设置为true
void afterNodeInsertion(boolean evict) { // possibly remove eldest
LinkedHashMap.Entry<K,V> first;
// 避免了最年长元素被删除的可能(就像一个普通的Map一样)
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;
}
其中,removeEldestEntry函数永远返回false,则永远不会删除头元素,如果改为true,在每次put完,都会删除掉头元素。
较为合理的实现实例,避免Map无限增长,可以自定义一个长度,那么配合AccessOrder=true,可以实现一个固定长度的LRU列表。
protected boolean removeEldestEntry(Map.Entry eldest){
return size() > 10;
}
7.LinkedHashMap重写函数
重写HashMap中Node相关操作,以维护LinkedHashMap内部的双向链表。
Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
LinkedHashMap.Entry<K,V> p =
new LinkedHashMap.Entry<K,V>(hash, key, value, e);
linkNodeLast(p);
return p;
}
Node<K,V> replacementNode(Node<K,V> p, Node<K,V> next) {
LinkedHashMap.Entry<K,V> q = (LinkedHashMap.Entry<K,V>)p;
LinkedHashMap.Entry<K,V> t =
new LinkedHashMap.Entry<K,V>(q.hash, q.key, q.value, next);
transferLinks(q, t);
return t;
}
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;
}
TreeNode<K,V> replacementTreeNode(Node<K,V> p, Node<K,V> next) {
LinkedHashMap.Entry<K,V> q = (LinkedHashMap.Entry<K,V>)p;
TreeNode<K,V> t = new TreeNode<K,V>(q.hash, q.key, q.value, next);
transferLinks(q, t);
return t;
}
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;
}
}
private void transferLinks(LinkedHashMap.Entry<K,V> src,
LinkedHashMap.Entry<K,V> dst) {
LinkedHashMap.Entry<K,V> b = dst.before = src.before;
LinkedHashMap.Entry<K,V> a = dst.after = src.after;
if (b == null)
head = dst;
else
b.after = dst;
if (a == null)
tail = dst;
else
a.before = dst;
}
8.迭代器
LinkedHashMap迭代,是对内部的双向链表进行迭代,从头元素到尾元素。
// Map返回键值对视图
public Set<Map.Entry<K,V>> entrySet() {
Set<Map.Entry<K,V>> es;
return (es = entrySet) == null ? (entrySet = new LinkedEntrySet()) : es;
}
// 键值对视图
final class LinkedEntrySet extends AbstractSet<Map.Entry<K,V>> {
public final int size() { return size; }
public final void clear() { LinkedHashMap.this.clear(); }
public final Iterator<Map.Entry<K,V>> iterator() {
// 键值对视图遍历,迭代器
return new LinkedEntryIterator();
}
public final boolean contains(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> e = (Map.Entry<?,?>) o;
Object key = e.getKey();
Node<K,V> candidate = getNode(hash(key), key);
return candidate != null && candidate.equals(e);
}
public final boolean remove(Object o) {
if (o instanceof Map.Entry) {
Map.Entry<?,?> e = (Map.Entry<?,?>) o;
Object key = e.getKey();
Object value = e.getValue();
return removeNode(hash(key), key, value, true, true) != null;
}
return false;
}
public final Spliterator<Map.Entry<K,V>> spliterator() {
return Spliterators.spliterator(this, Spliterator.SIZED |
Spliterator.ORDERED |
Spliterator.DISTINCT);
}
public final void forEach(Consumer<? super Map.Entry<K,V>> action) {
if (action == null)
throw new NullPointerException();
int mc = modCount;
for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after)
action.accept(e);
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
final class LinkedEntryIterator extends LinkedHashIterator
implements Iterator<Map.Entry<K,V>> {
public final Map.Entry<K,V> next() { return nextNode(); }
}
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;
}
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;
}
}