LinkedHashMap類似於HashMap
但是迭代遍歷它時
取得
鍵值對
的順序是插入次序
或者是最近最少使用(LRU)的次序
只比HashMap慢一點
而在迭代訪問時反而更快
因為它使用鏈表維護內部次序(HashMap是基於散列表實現的
相關HashMap的內容可以看《Java集合類》和《HashMap源碼分析》)
public class LinkedHashMap<K
V> extends HashMap<K
V> implements Map<K
V>
LinkedHashMap繼承自HashMap並實現了Map接口
LinkedHashMap只定義了兩個屬性
/**
* The head of the doubly linked list
* 雙向鏈表的頭節點
*/
private transient Entry<K
V> header;
/**
* The iteration ordering method for this linked hash map: true
* for access
order
false for insertion
order
* true表示最近最少使用次序
false表示插入順序
*/
private final boolean accessOrder;
LinkedList一共提供了五個構造方法
// 構造方法
構造一個指定初始容量和負載因子的
按照插入順序的LinkedList
public LinkedHashMap(int initialCapacity
float loadFactor) {
super(initialCapacity
loadFactor);
accessOrder = false;
}
// 構造方法
構造一個指定初始容量的LinkedHashMap
取得鍵值對的順序是插入順序
public LinkedHashMap(int initialCapacity) {
super(initialCapacity);
accessOrder = false;
}
// 構造方法
用默認的初始化容量和負載因子創建一個LinkedHashMap
取得鍵值對的順序是插入順序
public LinkedHashMap() {
super();
accessOrder = false;
}
// 構造方法
通過傳入的map創建一個LinkedHashMap
容量為默認容量(
)和(map
zise()/DEFAULT_LOAD_FACTORY)+
的較大者
裝載因子為默認值
public LinkedHashMap(Map<? extends K
? extends V> m) {
super(m);
accessOrder = false;
}
// 構造方法
根據指定容量
裝載因子和鍵值對保持順序創建一個LinkedHashMap
public LinkedHashMap(int initialCapacity
float loadFactor
boolean accessOrder) {
super(initialCapacity
loadFactor);
this
accessOrder = accessOrder;
}
從構造方法中可以看出
默認都采用插入順序來維持取出鍵值對的次序
所有構造方法都是通過調用父類的構造方法來創建對象的
LinkedHashMap是基於雙向鏈表的
而且屬性中定了一個header節點
為什麼構造方法都沒有對其進行初始化呢?
注意LinkedHashMap中有一個init()方法
HashMap的構造方法都調用了init()方法
這裡LinkedHashMap的構造方法在調用父類構造方法後將從父類構造方法中調用init()方法(這也解釋了為什麼HashMap中會有一個沒有內容的init()方法)
void init() {
header = new Entry<K
V>(
null
null
null);
header
before = header
after = header;
}
看init()方法
的確是對header進行了初始化
並構造成一個雙向循環鏈表(和LinkedList的存儲結構是一樣的)
transfer(HashMap
Entry[] newTable)方法和init()方法一樣也在HashTable中被調用
transfer(HashMap
Entry[] newTable)方法在HashMap調用resize(int newCapacity)方法的時候被調用
void transfer(HashMap
Entry[] newTable) {
int newCapacity = newTable
length;
for (Entry<K
V> e = header
after; e != header; e = e
after) {
int index = indexFor(e
hash
newCapacity);
e
next = newTable[index];
newTable[index] = e;
}
}
根據鏈表節點e的哈希值計算e在新容量的table數組中的索引
並將e插入到計算出的索引所引用的鏈表中
containsValue(Object value)
public boolean containsValue(Object value) {
// Overridden to take advantage of faster iterator
if (value==null) {
for (Entry e = header
after; e != header; e = e
after)
if (e
value==null)
return true;
} else {
for (Entry e = header
after; e != header; e = e
after)
if (value
equals(e
value))
return true;
}
return false;
}
重寫父類的containsValue(Object value)方法
直接通過header遍歷鏈表判斷是否有值和value相等
而不用查詢table數組
get(Object key)
public V get(Object key) {
Entry<K
V> e = (Entry<K
V>)getEntry(key);
if (e == null)
return null;
e
recordAccess(this);
return e
value;
}
get(Object key)方法通過HashMap的getEntry(Object key)方法獲取節點
並返回該節點的value值
獲取節點如果為null則返回null
recordAccess(HashMap<K
V> m)是LinkedHashMap的內部類Entry的一個方法
將在介紹Entry的時候進行詳細的介紹
clear()
public void clear() {
super
clear();
header
before = header
after = header;
}
clear()方法先調用父類的方法clear()方法
之後將鏈表的header節點的before和after引用都指向header自身
即header節點就是一個雙向循環鏈表
這樣就無法訪問到原鏈表中剩余的其他節點
他們都將被GC回收
以上的內容多多少少都涉及到了LinkedHashMap的內部類Entry<K
V>
下面詳細介紹這個內部類
// 這是一個私有的
靜態的內部類
繼承自HashMap的Entry
private static class Entry<K
V> extends HashMap
Entry<K
V> {
// 對前後節點的引用
Entry<K
V> before
after;
// 構造方法直接調用父類的構造方法
Entry(int hash
K key
V value
HashMap
Entry<K
V> next) {
super(hash
key
value
next);
}
// 移除該節點
只需修改前一節點的after引用和後一節點的before引用
private void remove() {
// 修改後該節點服務再被訪問
會被GC回收
before
after = after;
after
before = before;
}
// 在指定節點之前插入當前節點(雙向鏈表插入節點的過程)
private void addBefore(Entry<K
V> existingEntry) {
// 將當前節點的after引用指向existingEntry
after = existingEntry;
// 將before的引用指向existingEntry節點的前一節點
before = existingEntry
before;
// 將原先existingEntry節點的前一節點的after引用指向當前節點
before
after = this;
// 將原先existingEntry節點的後一節點的before引用指向當前節點
after
before = this;
}
// 當調用此類的get方法或put方法(put方法將調用到父類HashMap
Entry的put
// 方法)都將調用到recordAccess(HashMap<K
V> m)方法
// 如果accessOrder為true
即使用的是最近最少使用的次序
則將當前被修改的
// 節點移動到header節點之前
即鏈表的尾部
// 這也是為什麼在HashMap
Entry中有一個空的
// recordAccess(HashMap<K
V> m)方法的原因
void recordAccess(HashMap<K
V> m) {
LinkedHashMap<K
V> lm = (LinkedHashMap<K
V>)m;
if (lm
accessOrder) {
lm
modCount++;
remove();
addBefore(lm
header);
}
}
// 和recordAccess(HashMap<K
V> m)方法一樣
在HashMap
Entry中同樣有一個對應的空方法
當進行刪除(remove)操作的時候會被調用
void recordRemoval(HashMap<K
V> m) {
remove();
}
}
介紹完了內部類Entry
下面是創建一個Entry節點和添加一個Entry的兩個方法
createEntry(int hash
K key
V value
int bucketIndex)
void createEntry(int hash
K key
V value
int bucketIndex) {
HashMap
Entry<K
V> old = table[bucketIndex];
Entry<K
V> e = new Entry<K
V>(hash
key
value
old);
table[bucketIndex] = e;
e
addBefore(header);
size++;
}
createEntry(int hash
K key
V value
int bucketIndex)方法覆蓋了父類HashMap中的方法
這個方法不會拓展table數組的大小
該方法首先保留table中bucketIndex處的節點
然後調用Entry的構造方法(將調用到父類HashMap
Entry的構造方法)添加一個節點
即將當前節點的next引用指向table[bucketIndex] 的節點
之後調用的e
addBefore(header)是修改鏈表
將e節點添加到header節點之前
該方法同時在table[bucketIndex]的鏈表中添加了節點
也在LinkedHashMap自身的鏈表中添加了節點
addEntry(int hash K key V value int bucketIndex)
void addEntry(int hash
K key
V value
int bucketIndex) {
createEntry(hash
key
value
bucketIndex);
Entry<K
V> eldest = header
after;
if (removeEldestEntry(eldest)) {
removeEntryForKey(eldest
key);
} else {
if (size >= threshold)
resize(
* table
length);
}
}
首先調用createEntry(int hash
K key
V value
int bucketIndex)方法
之後獲取LinkedHashMap中
最老
(最近最少使用)的節點
接著涉及到了removeEldestEntry(Entry<K
V> eldest)方法
來看一下
protected boolean removeEldestEntry(Map
Entry<K
V> eldest) {
return false;
}
為什麼這個方法始終返回false?
結合上面的addEntry(int hash
K key
V value
int bucketIndex)方法
這樣設計可以使LinkedHashMap成為一個正常的Map
不會去移除
最老
的節點
為什麼不在代碼中直接去除這部分邏輯而是設計成這樣呢?
這為開發者提供了方便
若希望將Map當做Cache來使用
並且限制大小
只需繼承LinkedHashMap並重寫removeEldestEntry(Entry<K
V> eldest)方法
像這樣
private static final int MAX_ENTRIES =
;
protected boolean removeEldestEntry(Map
Entry eldest) {
return size() > MAX_ENTRIES;
}
LinkedHashMap除了以上內容外還有和迭代相關的三個方法及三個內部類以及一個抽象內部類
分別是
newKeyIterator()
newValueIterator()
newEntryIterator()和KeyIterator類
ValueIterator類
EntryIterator類以及LinkedHashIterator類
三個new方法分別返回對應的三個類的實例
而三個類都繼承自抽象類LinkedHashIterator
下面看迭代相關的三個類
private class KeyIterator extends LinkedHashIterator<K> {
public K next() { return nextEntry()
getKey(); }
}
private class ValueIterator extends LinkedHashIterator<V> {
public V next() { return nextEntry()
value; }
}
private class EntryIterator extends LinkedHashIterator<Map
Entry<K
V>> {
public Map
Entry<K
V> next() { return nextEntry(); }
}
從上面可以看出這三個類都很簡單
只有一個next()方法
next()方法也只是去調用LinkedHashIterator類中相應的方法
和KeyIterator類
ValueIterator類
EntryIterator類以及LinkedHashIterator類
下面是LinkedHashIterator類的內容
private abstract class LinkedHashIterator<T> implements Iterator<T> {
Entry<K
V> nextEntry = header
after;
Entry<K
V> lastReturned = null;
// 和LinkedList中ListItr類定義了expectedModCount用途一致
int expectedModCount = modCount;
// 下一個節點如果是header節點說明當前節點是鏈表的最後一個節點
即已經遍歷完鏈表了
沒有下一個節點了
public boolean hasNext() {
return nextEntry != header;
}
//移除上一次被返回的節點lastReturned
public void remove() {
if (lastReturned == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
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();
// 獲取並記錄返回的節點
Entry<K
V> e = lastReturned = nextEntry;
// 保存對下一個節點的引用
nextEntry = e
after;
return e;
}
}
LinkedHashMap本應和HashMap及LinkedList一起分析
比較他們的異同
為了彌補
這裡簡單的總結一些他們之間的異同
HashMap使用哈希表來存儲數據
並用拉鏈法來處理沖突
LinkedHashMap繼承自HashMap
同時自身有一個鏈表
使用鏈表存儲數據
不存在沖突
LinkedList和LinkedHashMap一樣使用一個雙向循環鏈表
但存儲的是簡單的數據
並不是
鍵值對
所以HashMap和LinkedHashMap是Map
而LinkedList是一個List
這是他們本質的區別
LinkedList和LinkedHashMap都可以維護內容的順序
但HashMap不維護順序
From:http://tw.wingwit.com/Article/program/Java/Javascript/201311/25431.html