在分析LinkedHashMap的源碼以前先看一下LinkedHashMap的繼承關係算法
經過上述繼承關係能夠看到,LinkedHashMap繼承自HashMap,也就是具備HashMap的全部功能,而後再看看源碼中的註釋:數組
從註釋中能夠了解到,LinkedHashMap本身維護了一個雙向鏈表,確切地說是一個雙向循環鏈表,下面看一下雙向循環鏈表安全
private static final long serialVersionUID = 3801124242820219131L;
//雙鏈表的頭結點
private transient LinkedHashMapEntry<K,V> header;
//雙鏈表的迭代順序,true表示按照訪問的順序,false表示插入的順序
private final boolean accessOrder;複製代碼
private static class LinkedHashMapEntry<K,V> extends HashMapEntry<K,V> {
// before爲指向上一個節點的指針,after爲指向下一個節點的指針
LinkedHashMapEntry<K,V> before, after;
//構造方法
LinkedHashMapEntry(int hash, K key, V value, HashMapEntry<K,V> next) {
super(hash, key, value, next);
}
//刪除某個節點,也就是將當前節點的前一個節點跟後一個節點鏈接在一塊兒
private void remove() {
//將上一個節點的尾指針指向下個節點
before.after = after;
//將下一個節點的頭指針指向上一個節點
after.before = before;
}
//在某個節點以前插入一個節點,結合上面你的示意圖比較好理解
private void addBefore(LinkedHashMapEntry<K,V> existingEntry) {
//將當前節點的after指針指向插圖的節點
after = existingEntry;
//將當前節點的before指針指向existingEntry的上一個節點
before = existingEntry.before;
//before的尾節點指向當前節點
before.after = this;
//after的頭結點指向當前節點
after.before = this;
}
//當一個已經存在的entry被get方法調用或者被set方法修改的時候此方法被父類(HashMap)調用,若是access-ordered爲true,那麼entry就會被移動到鏈表的尾部,不然不做處理。
void recordAccess(HashMap<K,V> m) {
//將HashMap強轉成LinkedHashMap
LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
if (lm.accessOrder) {
//若是accessOrder爲true
lm.modCount++;
//先移除此entry
remove();
//將lm添加到隊尾
addBefore(lm.header);
}
}
void recordRemoval(HashMap<K,V> m) {
remove();
}
}複製代碼
LinkedHashMap的構造方法其實就是間接調用了HashMap的構造方法,而後給迭代標誌位accessOrder一個false,也就是默認按照插入的順序進行排序,比較簡單bash
public LinkedHashMap() {
super();
accessOrder = false;
}複製代碼
public LinkedHashMap(int initialCapacity) {
super(initialCapacity);
accessOrder = false;
}複製代碼
public LinkedHashMap(int initialCapacity,
float loadFactor,
boolean accessOrder) {
super(initialCapacity, loadFactor);
this.accessOrder = accessOrder;
}複製代碼
public LinkedHashMap(Map<? extends K, ? extends V> m) {
super(m);
accessOrder = false;
}複製代碼
public V get(Object key) {
LinkedHashMapEntry<K,V> e = (LinkedHashMapEntry<K,V>)getEntry(key);
if (e == null)
return null;
//調用了get方法,就會將當前的entry放到鏈表的尾部,刪除的時候就會最後刪除
e.recordAccess(this);
return e.value;
}複製代碼
經過查找發現LinkedHashMap並無複寫Get方法,只是複寫了addEntry多線程
void addEntry(int hash, K key, V value, int bucketIndex) {
//這個是Android作的一些修改,略微不一樣於Java的SDK
LinkedHashMapEntry<K,V> eldest = header.after;
if (eldest != header) {
boolean removeEldest;
size++;
try {
//判斷是否移除最近最少使用的Entry
removeEldest = removeEldestEntry(eldest);
} finally {
size--;
}
if (removeEldest) {
//根據返回值是否移除最近最少使用的entry
removeEntryForKey(eldest.key);
}
}
//調用父類的addEntry
super.addEntry(hash, key, value, bucketIndex);
}複製代碼
//removeEldestEntry的返回值老是false,這個須要根據實際狀況來複寫,默認不移除併發
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
return false;
}複製代碼
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;
}複製代碼
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;
}複製代碼
仔細觀察一下發現,LinkedHashMap用雙鏈表自身的特性去遍歷了整個鏈表,從而替換了HashMap中的循環遍歷,這個是由於鏈表遍歷的效率更高。ide
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;
}
}
}複製代碼
@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;
}
}複製代碼
其實跟containsValue同樣,用自身的鏈表特性進行迭代,提升效率ui
插入一個元素,默認是插入到隊列的尾部this
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的迭代器spa
private abstract class LinkedHashIterator<T> implements Iterator<T> {
//默認的下個entry指的是鏈表的第1個entry節點
LinkedHashMapEntry<K,V> nextEntry = header.after;
//上一次返回的entry,也就是當前節點
LinkedHashMapEntry<K,V> lastReturned = null;
//用來判斷是不是多線程併發
int expectedModCount = modCount;
//是否還有下一個節點
public boolean hasNext() {
return nextEntry != header;
}
//移除掉某個entry節點
public void remove() {
if (lastReturned == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
//移除當前entry節點
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;
//返回當前節點的下一個節點
nextEntry = e.after;
return e;
}
}複製代碼
private class KeyIterator extends LinkedHashIterator<K> {
public K next() { return nextEntry().getKey(); }
}複製代碼
private class ValueIterator extends LinkedHashIterator<V> {
public V next() { return nextEntry().getValue(); }
}複製代碼
private class EntryIterator extends LinkedHashIterator<Map.Entry<K,V>> {
public Map.Entry<K,V> next() { return nextEntry(); }
}複製代碼
Key,Value,Entry分別複寫了next方法
到此爲止,基本上已經分析完成了LinkedHashedMap源碼的基本分析,主要仍是須要對雙向循環鏈表比較熟悉,它自己實際上也只是循環鏈表的一個實現類