一、总述
LinkedHashMap是一个有序的HashMap。HashMap源码分析请看《java 源码解析(01) HashMap》。
特点:
a、继承于HashMap,基于HashMap实现映射功能
b、增加对key的排序功能
b1、通过构造参数实现基于插入排序(key先插入的排在前面)
b2、通过构造参数实现基于访问排序(最后访问排在后面——LRU(Least Recently Used)算法)
LinkedHashMap的键值对存储结构(LinkedEntry)也是基于HashMap的键值对HashMapEntry扩展的
LinkedEntry在HashMapEntry基础上增加了两个属性,用于指向上一个和下一个的属性,这就构成了一个双向链表
注意:这个链表和HashMapEntry在HashMap里面维护的那个链表不一样。
LinkedEntry是基于Key的顺序产生的一个链表,下一个元素引用是LinkedEntry的nxt属性;
HashMapEntry是基于Key的hash地址一样的键值对构成的链表,下一个元素的引用还是HashMapEntry的next属性
也就是一堆元素(LinkedEntry也是HashMapEntry)他们之间关系存在于两种链表
LinkedEntry源码:
static class LinkedEntry<K, V>extends HashMapEntry<K, V> {
LinkedEntry<K, V> nxt;
LinkedEntry<K, V> prv;
LinkedEntry() { // 用于创建链表头
super(null, null, 0, null);
nxt = prv = this;
}
LinkedEntry(K key, V value, int hash,HashMapEntry<K, V> next, LinkedEntry<K, V> nxt, LinkedEntry<K,V> prv) {
super(key, value, hash, next);
this.nxt = nxt;
this.prv = prv;
}
}
二、LinkedHashMap的属性含义及作用
/** 双向链表(其实是一个环形双向链表)的链表头,该指针对应的元素不存储键值对信息 **/
transient LinkedEntry<K, V> header;
/** 是否基于访问排序 **/
private final boolean accessOrder;
三、LinkedHashMap的构造方法
1、带accessOrder的构造方法
其中参数initialCapacity和loadFactor用于构造父类HashMap,而参数accessOrder用于控制排序方式accessOrder 为true 基于访问排序;为false基于插入排序
public LinkedHashMap(intinitialCapacity, float loadFactor, boolean accessOrder) {
super(initialCapacity, loadFactor);
init();
this.accessOrder = accessOrder;
}
其中init方法用于初始化链表头
源码:
void init() {
header = new LinkedEntry<K, V>();
}
2、其他构造方法
这些构造都是构造出基于插入排序的LinkedHashMap,其他参数和HashMap构造方法一样
四、LinkedHashMap的操作API
1、put方法
该方法使用父类实现,这里只是重写了添加新键值对方法。步骤:
a、获取出最前面的元素,通过调用removeEldestEntry判断是否删除该元素,如果是直接删除
b、将新元素插入到链表尾
注:由于使用环形双向链表实现,所以链表头的前一个元素就是链表的最后元素,因此直接定位到最后一个位置插入
void addNewEntry(K key, V value, inthash, int index) {
LinkedEntry<K, V> header =this.header;
LinkedEntry<K, V> eldest =header.nxt;
if (eldest != header &&removeEldestEntry(eldest)) {
remove(eldest.key);
}
LinkedEntry<K, V> oldTail =header.prv;
LinkedEntry<K, V> newTail = newLinkedEntry<K,V>(key, value, hash, table[index], header, oldTail);
table[index] = oldTail.nxt = header.prv= newTail;
}
其中removeEldestEntry方法默认返回false,我们可以通过重写该方法实现true
源码:
protected booleanremoveEldestEntry(Map.Entry<K, V> eldest) {
return false;
}
2、get方法
步骤基本也是和HashMap一样,只是查找到了而且基于访问排序的话,就会调用makeTail方法重新排序
public V get(Object key) {
if (key == null) {
HashMapEntry<K, V> e =entryForNullKey;
if (e == null)
return null;
if (accessOrder)
makeTail((LinkedEntry<K,V>) e);
return e.value;
}
int hash = secondaryHash(key);
HashMapEntry<K, V>[] tab = table;
for (HashMapEntry<K, V> e =tab[hash & (tab.length - 1)];
e != null; e = e.next) {
K eKey = e.key;
if (eKey == key || (e.hash == hash&& key.equals(eKey))) {
if (accessOrder)
makeTail((LinkedEntry<K,V>) e);
return e.value;
}
}
return null;
}
makeTail方法:
将该元素转移到链表尾
private void makeTail(LinkedEntry<K, V> e) {
e.prv.nxt = e.nxt;
e.nxt.prv = e.prv;
LinkedEntry<K, V> header =this.header;
LinkedEntry<K, V> oldTail =header.prv;
e.nxt = header;
e.prv = oldTail;
oldTail.nxt = header.prv = e;
modCount++;
}
3、键值对被修改之前通知方法实现
该操作也会根据是否基于访问排序来重新排序双向链表
源码:
void preModify(HashMapEntry<K,V> e) {
if (accessOrder) {
makeTail((LinkedEntry<K, V>)e);
}
}
4、键值对配移除之后通知方法实现
从双向链表中删除该元素
源码:
void postRemove(HashMapEntry<K,V> e) {
LinkedEntry<K, V> le =(LinkedEntry<K, V>) e;
le.prv.nxt = le.nxt;
le.nxt.prv = le.prv;
le.nxt = le.prv = null; // Help the GC(for performance)
}
5、获取Map内部集合数据方法
原理也和HashMap一样,最主要的不同之处就是迭代器的实现
通过双向链表顺序进行迭代
源码:
private abstract classLinkedHashIterator<T> implements Iterator<T> {
LinkedEntry<K, V> next =header.nxt;
LinkedEntry<K, V> lastReturned =null;
int expectedModCount = modCount;
public final boolean hasNext() {
return next != header;
}
final LinkedEntry<K, V> nextEntry(){
if (modCount != expectedModCount)
throw newConcurrentModificationException();
LinkedEntry<K, V> e = next;
if (e == header)
throw newNoSuchElementException();
next = e.nxt;
return lastReturned = e;
}
public final void remove() {
if (modCount != expectedModCount)
throw newConcurrentModificationException();
if (lastReturned == null)
throw newIllegalStateException();
LinkedHashMap.this.remove(lastReturned.key);
lastReturned = null;
expectedModCount = modCount;
}
}
五、总结
LinkedHashMap比HashMap多维护了一个双向环形链表