概述
在分析LinkedHashMap的源碼之前先看一下LinkedHashMap的繼承關(guān)系
通過上述繼承關(guān)系可以看到,LinkedHashMap繼承自HashMap,也就是具有HashMap的所有功能,然后再看看源碼中的注釋:
- Hash table and linked list implementation of the Map interface,with predictable iteration order. This implementation differs from HashMap in that it maintains a doubly-linked list running through all of its entries. This linked list defines the iteration ordering, which is normally the order in which keys were inserted into the map. Note that insertion order is not affected if a key is re-inserted into the map.
- 一個(gè)實(shí)現(xiàn)了Map接口,并且可以知道迭代順序的哈希表個(gè)鏈表。這個(gè)實(shí)現(xiàn)跟HashMap的區(qū)別在于它維持了一個(gè)連接所有Entry的鏈表。這個(gè)鏈表定義了迭代的順序,默認(rèn)的迭代順序就是key被插入的順序。注意如果一個(gè)key被重新插入是不會(huì)影響鏈表的插入順序的。
從注釋中可以了解到,LinkedHashMap自己維護(hù)了一個(gè)雙向鏈表,確切地說是一個(gè)雙向循環(huán)鏈表,下面看一下雙向循環(huán)鏈表
- 雙向循環(huán)鏈表的結(jié)構(gòu)示意圖
- 雙向循環(huán)鏈表的元素插入
正文
成員變量
private static final long serialVersionUID = 3801124242820219131L;
//雙鏈表的頭結(jié)點(diǎn)
private transient LinkedHashMapEntry<K,V> header;
//雙鏈表的迭代順序,true表示按照訪問的順序,false表示插入的順序
private final boolean accessOrder;
LinkedHashMapEntry
private static class LinkedHashMapEntry<K,V> extends HashMapEntry<K,V> {
// before為指向上一個(gè)節(jié)點(diǎn)的指針,after為指向下一個(gè)節(jié)點(diǎn)的指針
LinkedHashMapEntry<K,V> before, after;
//構(gòu)造方法
LinkedHashMapEntry(int hash, K key, V value, HashMapEntry<K,V> next) {
super(hash, key, value, next);
}
//刪除某個(gè)節(jié)點(diǎn),也就是將當(dāng)前節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn)跟后一個(gè)節(jié)點(diǎn)連接在一起
private void remove() {
//將上一個(gè)節(jié)點(diǎn)的尾指針指向下個(gè)節(jié)點(diǎn)
before.after = after;
//將下一個(gè)節(jié)點(diǎn)的頭指針指向上一個(gè)節(jié)點(diǎn)
after.before = before;
}
//在某個(gè)節(jié)點(diǎn)之前插入一個(gè)節(jié)點(diǎn),結(jié)合上面你的示意圖比較好理解
private void addBefore(LinkedHashMapEntry<K,V> existingEntry) {
//將當(dāng)前節(jié)點(diǎn)的after指針指向插圖的節(jié)點(diǎn)
after = existingEntry;
//將當(dāng)前節(jié)點(diǎn)的before指針指向existingEntry的上一個(gè)節(jié)點(diǎn)
before = existingEntry.before;
//before的尾節(jié)點(diǎn)指向當(dāng)前節(jié)點(diǎn)
before.after = this;
//after的頭結(jié)點(diǎn)指向當(dāng)前節(jié)點(diǎn)
after.before = this;
}
//當(dāng)一個(gè)已經(jīng)存在的entry被get方法調(diào)用或者被set方法修改的時(shí)候此方法被父類(HashMap)調(diào)用,如果access-ordered為true,那么entry就會(huì)被移動(dòng)到鏈表的尾部,否則不作處理。
void recordAccess(HashMap<K,V> m) {
//將HashMap強(qiáng)轉(zhuǎn)成LinkedHashMap
LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
if (lm.accessOrder) {
//如果accessOrder為true
lm.modCount++;
//先移除此entry
remove();
//將lm添加到隊(duì)尾
addBefore(lm.header);
}
}
void recordRemoval(HashMap<K,V> m) {
remove();
}
}
構(gòu)造方法
LinkedHashMap的構(gòu)造方法其實(shí)就是間接調(diào)用了HashMap的構(gòu)造方法,然后給迭代標(biāo)志位accessOrder一個(gè)false,也就是默認(rèn)按照插入的順序進(jìn)行排序,比較簡(jiǎn)單
空的構(gòu)造方法
public LinkedHashMap() {
super();
accessOrder = false;
}
傳入一個(gè)容量
public LinkedHashMap(int initialCapacity) {
super(initialCapacity);
accessOrder = false;
}
傳入一個(gè)容量跟負(fù)載因子
public LinkedHashMap(int initialCapacity,
float loadFactor,
boolean accessOrder) {
super(initialCapacity, loadFactor);
this.accessOrder = accessOrder;
}
傳入一個(gè)Map
public LinkedHashMap(Map<? extends K, ? extends V> m) {
super(m);
accessOrder = false;
}
get方法
public V get(Object key) {
LinkedHashMapEntry<K,V> e = (LinkedHashMapEntry<K,V>)getEntry(key);
if (e == null)
return null;
//調(diào)用了get方法,就會(huì)將當(dāng)前的entry放到鏈表的尾部,刪除的時(shí)候就會(huì)最后刪除
e.recordAccess(this);
return e.value;
}
put方法
通過查找發(fā)現(xiàn)LinkedHashMap并沒有復(fù)寫Get方法,只是復(fù)寫了addEntry
void addEntry(int hash, K key, V value, int bucketIndex) {
//這個(gè)是Android做的一些修改,略微不同于Java的SDK
LinkedHashMapEntry<K,V> eldest = header.after;
if (eldest != header) {
boolean removeEldest;
size++;
try {
//判斷是否移除最近最少使用的Entry
removeEldest = removeEldestEntry(eldest);
} finally {
size--;
}
if (removeEldest) {
//根據(jù)返回值是否移除最近最少使用的entry
removeEntryForKey(eldest.key);
}
}
//調(diào)用父類的addEntry
super.addEntry(hash, key, value, bucketIndex);
}
//removeEldestEntry的返回值總是false,這個(gè)需要根據(jù)實(shí)際情況來復(fù)寫,默認(rèn)不移除
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
return false;
}
containsValue
HashMap
public boolean containsValue(Object value) {
if (value == null)
return containsNullValue();
HashMapEntry[] tab = table;
for (int i = 0; i < tab.length ; i++)
for (HashMapEntry e = tab[i] ; e != null ; e = e.next)
if (value.equals(e.value))
return true;
return false;
}
private boolean containsNullValue() {
HashMapEntry[] tab = table;
for (int i = 0; i < tab.length ; i++)
for (HashMapEntry e = tab[i] ; e != null ; e = e.next)
if (e.value == null)
return true;
return false;
}
LinkedHashMap
public boolean containsValue(Object value) {
// Overridden to take advantage of faster iterator
if (value==null) {
for (LinkedHashMapEntry e = header.after; e != header; e = e.after)
if (e.value==null)
return true;
} else {
for (LinkedHashMapEntry e = header.after; e != header; e = e.after)
if (value.equals(e.value))
return true;
}
return false;
}
仔細(xì)觀察一下發(fā)現(xiàn),LinkedHashMap用雙鏈表自身的特性去遍歷了整個(gè)鏈表,從而替換了HashMap中的循環(huán)遍歷,這個(gè)是因?yàn)殒湵肀闅v的效率更高。
- HashMap底層是Entry數(shù)組,所以循環(huán)遍歷效率高,通過下標(biāo)可以直接拿到對(duì)應(yīng)的元素
- LinkedHashMap底層是鏈表,無法通過下標(biāo)拿到對(duì)應(yīng)的元素,所以只能挨個(gè)遍歷,效率較低,所以通過指針遍歷效率會(huì)高一些
transfer方法:擴(kuò)容
HashMap
void transfer(HashMapEntry[] newTable) {
int newCapacity = newTable.length;
for (HashMapEntry<K, V> e : table) {
while (null != e) {
HashMapEntry<K, V> next = e.next;
int i = indexFor(e.hash, newCapacity);
e.next = newTable[i];
newTable[i] = e;
e = next;
}
}
}
LinkedHashMap
@Override
void transfer(HashMapEntry[] newTable) {
int newCapacity = newTable.length;
for (LinkedHashMapEntry<K,V> e = header.after; e != header; e = e.after) {
int index = indexFor(e.hash, newCapacity);
e.next = newTable[index];
newTable[index] = e;
}
}
其實(shí)跟containsValue一樣,用自身的鏈表特性進(jìn)行迭代,提高效率
createEntry
插入一個(gè)元素,默認(rèn)是插入到隊(duì)列的尾部
void createEntry(int hash, K key, V value, int bucketIndex) {
HashMapEntry<K,V> old = table[bucketIndex];
LinkedHashMapEntry<K,V> e = new LinkedHashMapEntry<>(hash, key, value, old);
table[bucketIndex] = e;
e.addBefore(header);
size++;
}
迭代
LinkedHashMap完全重寫了HashMap的迭代器
LinkedHashIterator
private abstract class LinkedHashIterator<T> implements Iterator<T> {
//默認(rèn)的下個(gè)entry指的是鏈表的第1個(gè)entry節(jié)點(diǎn)
LinkedHashMapEntry<K,V> nextEntry = header.after;
//上一次返回的entry,也就是當(dāng)前節(jié)點(diǎn)
LinkedHashMapEntry<K,V> lastReturned = null;
//用來判斷是否是多線程并發(fā)
int expectedModCount = modCount;
//是否還有下一個(gè)節(jié)點(diǎn)
public boolean hasNext() {
return nextEntry != header;
}
//移除掉某個(gè)entry節(jié)點(diǎn)
public void remove() {
if (lastReturned == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
//移除當(dāng)前entry節(jié)點(diǎn)
LinkedHashMap.this.remove(lastReturned.key);
lastReturned = null;
expectedModCount = modCount;
}
Entry<K,V> nextEntry() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
if (nextEntry == header)
throw new NoSuchElementException();
LinkedHashMapEntry<K,V> e = lastReturned = nextEntry;
//返回當(dāng)前節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)
nextEntry = e.after;
return e;
}
}
KeyIterator
private class KeyIterator extends LinkedHashIterator<K> {
public K next() { return nextEntry().getKey(); }
}
ValueIterator
private class ValueIterator extends LinkedHashIterator<V> {
public V next() { return nextEntry().getValue(); }
}
EntryIterator
private class EntryIterator extends LinkedHashIterator<Map.Entry<K,V>> {
public Map.Entry<K,V> next() { return nextEntry(); }
}
Key,Value,Entry分別復(fù)寫了next方法
到此為止,基本上已經(jīng)分析完成了LinkedHashedMap源碼的基本分析,主要還是需要對(duì)雙向循環(huán)鏈表比較熟悉,它本身實(shí)際上也只是循環(huán)鏈表的一個(gè)實(shí)現(xiàn)類
總結(jié)
- LinkedHashMap繼承自HashMap,非線程安全
- LinkedHashMap自己維護(hù)了一個(gè)雙向循環(huán)鏈表,有一個(gè)head指針,通過將最近最少使用的元素放到隊(duì)列的頭部,新插入的以及經(jīng)常使用的元素放到尾部來幫助實(shí)現(xiàn)LRU算法
- LinkedHashMap有一個(gè)排序的標(biāo)志位accessOrder,默認(rèn)為false,即按照插入的順序進(jìn)行排序,如果為true,就將該元素放到隊(duì)列尾部
- 雖然LinkedHashMap并沒有實(shí)現(xiàn)put方法,但是覆蓋了addEntry方法,實(shí)際上并沒有什么用
- LRU算法的實(shí)現(xiàn)也是利用了LinkedHashMap的accessOrder標(biāo)志位,同時(shí)也必須復(fù)寫removeEldestEntry方法,根據(jù)實(shí)際需求決定是否刪除最近最少使用的元素。