一、LinkedHashMap的屬性
二、LinkedHashMap的構(gòu)造方法
三、LinkedHashMap的重要函數(shù)
1. afterNodeAccess函數(shù)
2. afterNodeInsertion函數(shù)
3. afterNodeRemoval函數(shù)
4. transferLinks函數(shù)
5. linkNodeLast函數(shù)
6. 其他方法
四、LinkedHashMap的迭代器
1. 基礎(chǔ)迭代器--LinkedHashIterator
2. 鍵迭代器--LinkedKeyIterator
3. 值迭代器--LinkedValueIterator
4. 結(jié)點迭代器--LinkedEntryIterator
LinkedHashMap<K,V>繼承HashMap<K,V>類,實現(xiàn)了 Map<K,V>接口。
LinkedHashMap是HashMap的子類,與HashMap有著同樣的存儲結(jié)構(gòu),但它加入了一個雙向鏈表的頭結(jié)點,將所有put到LinkedHashmap的節(jié)點一一串成了一個雙向循環(huán)鏈表,因此它保留了節(jié)點插入的順序,可以使節(jié)點的輸出順序與輸入順序相同。
LinkedHashMap可以用來實現(xiàn)LRU算法(這會在下面的源碼中進行分析)。
LinkedHashMap同樣是非線程安全的,只在單線程環(huán)境下使用。
1. LinkedHashMap的屬性
/**
* 靜態(tài)內(nèi)部類Entry表示雙向鏈表,繼承自HashMap的Node,在其基礎(chǔ)上加上了before和after兩個指針
* 雙向循環(huán)鏈表的頭結(jié)點,整個LinkedHashMap中只有一個header
* 它將哈希表中所有的Entry貫穿起來,header中不保存key-value對,只保存前后節(jié)點的引用
*/
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);
}
}
- private static final long serialVersionUID = 3801124242820219131L;
序列化 - transient LinkedHashMap.Entry<K,V> head;
鏈表頭結(jié)點 - transient LinkedHashMap.Entry<K,V> tail;
鏈表尾結(jié)點 - final boolean accessOrder;
雙向鏈表中元素排序規(guī)則的標(biāo)志位;
false:表示按插入順序排序;true:表示按訪問順序排序
2. LinkedHashMap的構(gòu)造方法
2.1 LinkedHashMap(int, float)型構(gòu)造函數(shù)
/**
* 總是會在構(gòu)造函數(shù)的第一行調(diào)用父類構(gòu)造函數(shù),使用super關(guān)鍵字,
* accessOrder默認(rèn)為false,access為true表示之后訪問順序按照元素的訪問順序進行
* 即不按照之前的插入順序了,access為false表示按照插入順序訪問。
*/
public LinkedHashMap(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
accessOrder = false;
}
2.2 LinkedHashMap(int)型構(gòu)造函數(shù)
/**
* 加載因子取默認(rèn)的0.75f
*/ public LinkedHashMap(int initialCapacity) {
super(initialCapacity);
accessOrder = false;
}
2.3 LinkedHashMap()型構(gòu)造函數(shù)
/**
* 加載因子取默認(rèn)的0.75f,容量取默認(rèn)的16
*/
public LinkedHashMap() {
super();
accessOrder = false;
}
2.4 LinkedHashMap(Map<? extends K, ? extends V>)型構(gòu)造函數(shù)
/**
* putMapEntries是調(diào)用到父類HashMap的構(gòu)造函數(shù)
*/
public LinkedHashMap(Map<? extends K, ? extends V> m) {
super();
accessOrder = false;
putMapEntries(m, false);
}
2.5 LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder)型構(gòu)造函數(shù)
/**
* 可以指定accessOrder的值,從而控制訪問順序
*/
public LinkedHashMap(int initialCapacity,
float loadFactor,
boolean accessOrder) {
super(initialCapacity, loadFactor);
this.accessOrder = accessOrder;
}
3. LinkedHashMap的重要函數(shù)
- afterNodeAccess、afterNodeInsertion、afterNodeRemoval這三個方法都是重寫父類HashMap的方法。
3.1 afterNodeAccess函數(shù)
/**
* 把當(dāng)前節(jié)點e放到雙向鏈表尾部
*/
void afterNodeAccess(Node<K,V> e) { // move node to last
LinkedHashMap.Entry<K,V> last;
/* accessOrder就是我們前面說的LRU控制,當(dāng)它為true,同時e對象不是尾節(jié)點
(如果訪問尾節(jié)點就不需要設(shè)置,該方法就是把節(jié)點放置到尾節(jié)點)
*/
if (accessOrder && (last = tail) != e) {
// 用a和b分別記錄該節(jié)點前面和后面的節(jié)點
LinkedHashMap.Entry<K,V> p =
(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
// 釋放當(dāng)前節(jié)點與后節(jié)點的關(guān)系
p.after = null;
// 如果當(dāng)前節(jié)點的前節(jié)點是空
if (b == null)
// 那么頭節(jié)點就設(shè)置為a
head = a;
else
// 如果b不為null,那么b的后節(jié)點指向a
b.after = a;
// 如果a節(jié)點不為空
if (a != null)
// a的后節(jié)點指向b
a.before = b;
else
// 如果a為空,那么b就是尾節(jié)點
last = b;
// 如果尾節(jié)點為空
if (last == null)
// 那么p為頭節(jié)點
head = p;
else {
// 否則就把p放到雙向鏈表最尾處
p.before = last;
last.after = p;
}
// 設(shè)置尾節(jié)點為P
tail = p;
// LinkedHashMap對象操作次數(shù)+1
++modCount;
}
}
此函數(shù)在很多函數(shù)(如put)中都會被回調(diào),LinkedHashMap重寫了HashMap中的此函數(shù)。若訪問順序為true,且訪問的對象不是尾結(jié)點
訪問結(jié)點3的前后狀態(tài)
從圖中可以看到,結(jié)點3鏈接到了尾結(jié)點后面
LinkedHashMap重寫了HashMap的get方法:
public V get(Object key) {
Node<K,V> e;
if ((e = getNode(hash(key), key)) == null)
return null;
if (accessOrder) // 如果啟用了LRU規(guī)則
afterNodeAccess(e); // 那么把該節(jié)點移到雙向鏈表最后面
return e.value;
}
- 注: LinkedHashMap壓根沒有重寫put方法,每次用LinkedHashMap的put方法的時候,其實你調(diào)用的都是HashMap的put方法。但它也會執(zhí)行afterNodeAccess()方法,因為這個方法HashMap就是存在的,但是沒有實現(xiàn),LinkedHashMap重寫了afterNodeAccess()這個方法。
3.2 afterNodeInsertion函數(shù)
/**
* 在哈希表中插入了一個新節(jié)點時調(diào)用的,它會把鏈表的頭節(jié)點刪除掉,刪除的方式是通過調(diào)用HashMap的removeNode方法
*/
void afterNodeInsertion(boolean evict) { // possibly remove eldest
LinkedHashMap.Entry<K,V> first;
if (evict && (first = head) != null && removeEldestEntry(first)) {
K key = first.key;
removeNode(hash(key), key, null, false, true);
}
}
3.3 afterNodeRemoval函數(shù)
/**
* 當(dāng)HashMap刪除一個鍵值對時調(diào)用的,它會把在HashMap中刪除的那個鍵值對一并從鏈表中刪除,保證了哈希表和鏈表的一致性。
*/
void afterNodeInsertion(boolean evict) { // possibly remove eldest
LinkedHashMap.Entry<K,V> first;
if (evict && (first = head) != null && removeEldestEntry(first)) {
K key = first.key;
removeNode(hash(key), key, null, false, true);
}
}
3.4 transferLinks函數(shù)
/**
* 用dst替換src
*/
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;
}
使用結(jié)點7替換結(jié)點3
3.5 linkNodeLast函數(shù)
在看linkNodeLast函數(shù)之前先看看newNode和newTreeNode這兩個函數(shù)
3.5.1 newNode函數(shù)
/**
* 當(dāng)桶中結(jié)點類型為HashMap.Node類型時,調(diào)用此函數(shù)
*/
Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
// 生成Node結(jié)點
LinkedHashMap.Entry<K,V> p =
new LinkedHashMap.Entry<K,V>(hash, key, value, e);
// 將該結(jié)點插入雙鏈表末尾
linkNodeLast(p);
return p;
}
3.5.2 newTreeNode函數(shù)
/**
* 當(dāng)桶中結(jié)點類型為HashMap.TreeNode時,調(diào)用此函數(shù)
*/
TreeNode<K,V> newTreeNode(int hash, K key, V value, Node<K,V> next) {
// 生成TreeNode結(jié)點
TreeNode<K,V> p = new TreeNode<K,V>(hash, key, value, next);
// 將該結(jié)點插入雙鏈表末尾
linkNodeLast(p);
return p;
}
3.5.3 linkNodeLast函數(shù)
/**
* 把新加的節(jié)點放在鏈表的最后面
*/
private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
// 將tail給臨時變量last
LinkedHashMap.Entry<K,V> last = tail;
// 把new的Entry給tail
tail = p;
// 若沒有l(wèi)ast,說明p是第一個節(jié)點,head=p
if (last == null)
head = p;
else {
p.before = last;
last.after = p;
}
}
3.6 其他方法
- internalWriteEntries方法
/**
* 該方法也是重寫父類HashMap的方法,也是為了LinkedHashMap鍵和值被序列化的存儲順序
*/
void internalWriteEntries(java.io.ObjectOutputStream s) throws IOException {
for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) {
s.writeObject(e.key);
s.writeObject(e.value);
}
}
其他方法就不一一介紹的官方API中都有詳細(xì)的說明。
4. LinkedHashMap的迭代器
4.1基礎(chǔ)迭代器--LinkedHashIterator
為抽象類,用于對LinkedHashMap進行迭代
/**
* LinkedHashIterator是LinkedHashMap的迭代器,為抽象類,用于對LinkedHashMap進行迭代
*/
abstract class LinkedHashIterator {
// 下一個結(jié)點
LinkedHashMap.Entry<K,V> next;
// 當(dāng)前結(jié)點
LinkedHashMap.Entry<K,V> current;
// 期望的修改次數(shù)
int expectedModCount;
LinkedHashIterator() {
// next賦值為頭結(jié)點
next = head;
// 賦值修改次數(shù)
expectedModCount = modCount;
// 當(dāng)前結(jié)點賦值為空
current = null;
}
// 是否存在下一個結(jié)點
public final boolean hasNext() {
return next != null;
}
final LinkedHashMap.Entry<K,V> nextNode() {
LinkedHashMap.Entry<K,V> e = next;
// 檢查是否存在結(jié)構(gòu)性修改
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
// 當(dāng)前結(jié)點是否為空
if (e == null)
throw new NoSuchElementException();
// 賦值當(dāng)前節(jié)點
current = e;
// 賦值下一個結(jié)點
next = e.after;
return e;
}
public final void remove() {
// 保存當(dāng)前結(jié)點
Node<K,V> p = current;
if (p == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
current = null;
K key = p.key;
// 移除結(jié)點
removeNode(hash(key), key, null, false, false);
// 更新最新修改數(shù)
expectedModCount = modCount;
}
}
4.2 鍵迭代器--LinkedKeyIterator
繼承自LinkedHashIterator,實現(xiàn)了Iterator接口,對LinkedHashMap中的鍵進行迭代
final class LinkedKeyIterator extends LinkedHashIterator
implements Iterator<K> {
public final K next() { return nextNode().getKey(); }
}
4.3 值迭代器--LinkedValueIterator
繼承自LinkedHashIterator,實現(xiàn)了Iterator接口,對LinkedHashMap中的值進行迭代
final class LinkedValueIterator extends LinkedHashIterator
implements Iterator<V> {
public final V next() { return nextNode().value; }
}
4.4 結(jié)點迭代器--LinkedEntryIterator
繼承自LinkedHashIterator,實現(xiàn)了Iterator接口,對LinkedHashMap中的結(jié)點進行迭代
final class LinkedEntryIterator extends LinkedHashIterator
implements Iterator<Map.Entry<K,V>> {
public final Map.Entry<K,V> next() { return nextNode(); }
}