LinkedHashMap简介
LinkedHashMap继承自HashMap,底层仍然是数组加链表,除了发生冲突后的单链表,它还维护了一个链接所有元素的双链表,这样就能记录元素的插入或者访问顺序了。所以LinkedHashMap可以做到有序迭代,也可以作为LRU算法的基石。
数据结构
LinkedEntry<K, V> header;
static class LinkedEntry<K, V> extends HashMapEntry<K, V> {
LinkedEntry<K, V> nxt;
LinkedEntry<K, V> prv;
/** Create the header entry */
LinkedEntry() {
super(null, null, 0, null);
nxt = prv = this;
}
/** Create a normal entry */
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;
}
}
LinkedEntry继承自HashMapEntry
next属性仍然组织发生冲突后的单链表。
nxt和prv两个属性组织一个所有元素的双链表。这样就能记录插入顺序或者访问顺序。
header是双向循环链表的起点,和HashMap中的entryForNullKey一样,它游离于table之外。
LinkedHashMap其实存储了两套逻辑,一套是原封不动的HashMap的逻辑。
另一套是一个双向链表的逻辑。
put方法
LinkedHashMap并没有重写HashMap的put方法,而只是重写了其中最后的addNewEntry方法。
因为除了组织双向链表的逻辑,其他地方都和HashMap一样。
void addNewEntry(K key, V value, int hash, int index) {
LinkedEntry<K, V> header = this.header;
// Remove eldest entry if instructed to do so.
LinkedEntry<K, V> eldest = header.nxt;
// 默认的removeEldestEntry直接返回了false
if (eldest != header && removeEldestEntry(eldest)) {
remove(eldest.key);
}
// Create new entry, link it on to list, and put it into table
LinkedEntry<K, V> oldTail = header.prv;
LinkedEntry<K, V> newTail = new LinkedEntry<K,V>(
key, value, hash, table[index], header, oldTail);
table[index] = oldTail.nxt = header.prv = newTail;
}
第叉叉行,无非也就是一个双向链表的插入方法。如果这个过程不熟悉,去看一LinkedList里的add方法就豁然开朗了。
makeTail方法
LinkedHashMap的默认顺序是插入顺序,如果要实现访问顺序,那么需要将全局的accessOrder属性置为true。
当然LinkedHashMap提供了带accessOrder参数的构造函数。访问顺序是在调用get等方法的时候内部调用了makeTail方法,
非常直观,也就是把要访问的Entry放到尾部去。
private void makeTail(LinkedEntry<K, V> e) {
// 把e从链表里拿出来
e.prv.nxt = e.nxt;
e.nxt.prv = e.prv;
// 把e链接在尾部
LinkedEntry<K, V> header = this.header;
LinkedEntry<K, V> oldTail = header.prv;
e.nxt = header;
e.prv = oldTail;
oldTail.nxt = header.prv = e;
modCount++;
}
makeTail方法分两步实现,第一是把元素取出来,第二是插入到尾部。虽然这样写非常像一段台词,
但实际情况就是这样
get方法每次都调用,put方法中如果key相同会替换掉value,在替换之前会调用preModify方法,
preModify在LinkedHashMap中的实现是调用这个方法
关于双向链表的删除和插入操作,请看。
public class LinkedHashMap<K, V> extends HashMap<K, V> {
// 头Entry,不包含任何数据。
transient LinkedEntry<K, V> header;
// true是访问排序,false是按插入顺序排。
private final boolean accessOrder;
// 构造函数,默认是插入顺序。
public LinkedHashMap() {
init();
accessOrder = false;
}
// 指定容量
public LinkedHashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
// 指定容量和加载因子,内部调用三个参数的构造函数。
// 三个参数的构造函数调用 super(initialCapacity, loadFactor) 也就是HashMap的构造函数
// loadFactor会被忽略掉,然后自动使用0.75的。
public LinkedHashMap(int initialCapacity, float loadFactor) {
this(initialCapacity, loadFactor, false);
}
// 调用HashMap的构造方法,来构造table数组
public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) {
super(initialCapacity, loadFactor);
init();
this.accessOrder = accessOrder;
}
@Override void init() {
header = new LinkedEntry<K, V>();
}
// 双向链表的数据结构,这个感觉和LinkedList的双向链表感觉感谢
static class LinkedEntry<K, V> extends HashMapEntry<K, V> {
LinkedEntry<K, V> nxt;
LinkedEntry<K, V> prv;
/** Create the header entry */
LinkedEntry() {
super(null, null, 0, null);
nxt = prv = this;
}
/** Create a normal entry */
LinkedEntry(K key, V value, int hash, HashMapEntry<K, V> next,
LinkedEntry<K, V> nxt, LinkedEntry<K, V> prv) {
//调用super后,发生了什么?
super(key, value, hash, next);
this.nxt = nxt;
this.prv = prv;
}
}
// 这个this是什么意思
final K key;
V value;
final int hash;
HashMapEntry<K, V> next;
HashMapEntry(K key, V value, int hash, HashMapEntry<K, V> next) {
this.key = key;
this.value = value;
this.hash = hash;
this.next = next;
}
/**
* Returns the eldest entry in the map, or {@code null} if the map is empty.
* @hide
*/
public Entry<K, V> eldest() {
LinkedEntry<K, V> eldest = header.nxt;
return eldest != header ? eldest : null;
}
// 新add的是在尾部
@Override void addNewEntry(K key, V value, int hash, int index) {
LinkedEntry<K, V> header = this.header;
// Remove eldest entry if instructed to do so.
LinkedEntry<K, V> eldest = header.nxt;
if (eldest != header && removeEldestEntry(eldest)) {
remove(eldest.key);
}
// Create new entry, link it on to list, and put it into table
LinkedEntry<K, V> oldTail = header.prv;
LinkedEntry<K, V> newTail = new LinkedEntry<K,V>(
key, value, hash, table[index], header, oldTail);
table[index] = oldTail.nxt = header.prv = newTail;
}
@Override void addNewEntryForNullKey(V value) {
LinkedEntry<K, V> header = this.header;
// Remove eldest entry if instructed to do so.
LinkedEntry<K, V> eldest = header.nxt;
if (eldest != header && removeEldestEntry(eldest)) {
remove(eldest.key);
}
// Create new entry, link it on to list, and put it into table
LinkedEntry<K, V> oldTail = header.prv;
LinkedEntry<K, V> newTail = new LinkedEntry<K,V>(
null, value, 0, null, header, oldTail);
entryForNullKey = oldTail.nxt = header.prv = newTail;
}
/**
* As above, but without eviction.
*/
@Override HashMapEntry<K, V> constructorNewEntry(
K key, V value, int hash, HashMapEntry<K, V> next) {
LinkedEntry<K, V> header = this.header;
LinkedEntry<K, V> oldTail = header.prv;
LinkedEntry<K, V> newTail
= new LinkedEntry<K,V>(key, value, hash, next, header, oldTail);
return oldTail.nxt = header.prv = newTail;
}
/**
* Returns the value of the mapping with the specified key.
*
* @param key
* the key.
* @return the value of the mapping with the specified key, or {@code null}
* if no mapping for the specified key is found.
*/
// get方法复写
@Override 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 = Collections.secondaryHash(key);
// 索引和HashMap一样
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;
}
/**
* Relinks the given entry to the tail of the list. Under access ordering,
* this method is invoked whenever the value of a pre-existing entry is
* read by Map.get or modified by Map.put.
*/
private void makeTail(LinkedEntry<K, V> e) {
// Unlink e
// 把e从链表里拿出来
e.prv.nxt = e.nxt;
e.nxt.prv = e.prv;
// Relink e as tail
// 然后把e放到尾部
LinkedEntry<K, V> header = this.header;
LinkedEntry<K, V> oldTail = header.prv;
e.nxt = header;
e.prv = oldTail;
oldTail.nxt = header.prv = e;
modCount++;
}
@Override void preModify(HashMapEntry<K, V> e) {
if (accessOrder) {
makeTail((LinkedEntry<K, V>) e);
}
}
@Override 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)
}
//
public void clear() {
// HashMap的clear方法
// 把table里都装上null
super.clear();
// Clear all links to help GC
// 从header开始遍历清空
LinkedEntry<K, V> header = this.header;
for (LinkedEntry<K, V> e = header.nxt; e != header; ) {
LinkedEntry<K, V> nxt = e.nxt;
e.nxt = e.prv = null;
e = nxt;
}
// 回归到初始的空map状态
header.nxt = header.prv = header;
}
}