LinkedHashMap原始碼解讀
LinkedHashMap是HashMap的子類,唯一的區別在於LinkedHashMap對順序的維護,是有序的
建構函式
public LinkedHashMap(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
accessOrder = false;
}
public LinkedHashMap(int initialCapacity) {
super(initialCapacity);
accessOrder = false;
}
public LinkedHashMap() {
super();
accessOrder = false;
}
public LinkedHashMap(Map<? extends K, ? extends V> m) {
super(m);
accessOrder = false;
}
public LinkedHashMap(int initialCapacity,
float loadFactor,
boolean accessOrder) {
super(initialCapacity, loadFactor);
this.accessOrder = accessOrder;
}
建構函式引數容量,載入因子都繼承hashMap,唯一的區分在於對於屬性accessOrder的維護
/**
* The iteration ordering method for this linked hash map: <tt>true</tt>
* for access-order, <tt>false</tt> for insertion-order.
*
* @serial
*/
private final boolean accessOrder;
accessOrder欄位表示對順序的維護。true表示按照訪問順序,false表示按照插入順序(插入可能不是在尾部順序新增,可能是按照索引新增,順序可能不一致)
LinkedHashMap構造預設是維護元素插入的順序
linkedHashMap的資料結構的最小單元
private static class Entry<K,V> extends HashMap.Entry<K,V> {
// These fields comprise the doubly linked list used for iteration.
Entry<K,V> before, after;
Entry(int hash, K key, V value, HashMap.Entry<K,V> next) {
super(hash, key, value, next);
}
/**
* Removes this entry from the linked list.
*/
private void remove() {
before.after = after;
after.before = before;
}
在HashMap.Entry的基礎上新增了兩個屬性before,after 表示 順序上的前一個元素的地址和後一個元素的地址,而原next只是表示在一個陣列位置處的連結串列上的,下一個元素位置。
而且,LinkedHashMap對構造器中的init方法重寫
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
threshold = initialCapacity;
init();
}
@Override
void init() {
header = new Entry<>(-1, null, null, null);
header.before = header.after = header;
}
構造LinkedHashMap的時候就會建立一個header節點,不放在陣列中,只是用來維護順序
存放元素
public V put(K key, V value) {
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
if (key == null)
return putForNullKey(value);
int hash = hash(key);
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
}
LinkedHashMap重寫了recordAccess方法void recordAccess(HashMap<K,V> m) {
LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
if (lm.accessOrder) {
lm.modCount++;
remove();
addBefore(lm.header);
}
}
private void remove() {
before.after = after;
after.before = before;
}
private void addBefore(Entry<K,V> existingEntry) {
after = existingEntry;
before = existingEntry.before;
before.after = this;
after.before = this;
}
如果accessOrder為true,就是維護訪問的順序而且,不論是accessOrder是true還是false都是會呼叫addBefore方法,將新加的元素放到連結串列末尾
void createEntry(int hash, K key, V value, int bucketIndex) {
HashMap.Entry<K,V> old = table[bucketIndex];
Entry<K,V> e = new Entry<>(hash, key, value, old);
table[bucketIndex] = e;
e.addBefore(header);
size++;
}
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;
}
查詢元素,如果AccessOrder=true,在順序連結串列,從上面的分析可以看出,當前查詢的元素會放到雙向連結串列的末尾。,最不常用的元素會靠近header。abstract class LinkedHashIterator {
LinkedHashMap.Entry<K,V> next;
LinkedHashMap.Entry<K,V> current;
int expectedModCount;
LinkedHashIterator() {
next = head;
expectedModCount = modCount;
current = null;
}
public final boolean hasNext() {
return next != null;
}
final LinkedHashMap.Entry<K,V> nextNode() {
LinkedHashMap.Entry<K,V> e = next;
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
if (e == null)
throw new NoSuchElementException();
current = e;
next = e.after;
return e;
}
public final void remove() {
Node<K,V> p = current;
if (p == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
current = null;
K key = p.key;
removeNode(hash(key), key, null, false, false);
expectedModCount = modCount;
}
}
這類是LinkedHashMap實現迭代的最上層的類,如果modCount!=expectedModCount就是丟擲異常,就是fast-fail機制,
這種機制,使得資料結構不能在迭代的期間發生更改,也就不是執行緒安全的。
* LinkedHashMap錯誤的迭代方式,按照hashMap取出key,在獲取值
public static void loopHashMap(LinkedHashMap<String, String> map ){
Set<String> keySet = map.keySet();
Iterator<String> iterator = keySet.iterator();
while(iterator.hasNext()){
System.out.print(map.get(iterator.next()));
}
System.out.println();
}
public static void main(String[] args) {
LinkedHashMap<String, String> lhm = new LinkedHashMap<String, String>(16, 0.75f, true);
loopHashMap(lhm);
}
LinkedHashMap呼叫get方法的時候,如果accessOrder=true,會呼叫recordAccess修改modCount數值,導致modCount!=exceptedModCount丟擲異常,不建議使用key,在獲取值的方式進行遍歷。
* 正確的迭代方式
public static void loopHashMap(LinkedHashMap<String, String> map ){
Set<Entry<String, String>> entrySet = map.entrySet();
Iterator<Entry<String, String>> iterator = entrySet.iterator();
while(iterator.hasNext()){
System.out.print(iterator.next().getValue());
}
System.out.println();
}
驗證LinkedHashMap,在accessOrder為true的順序,當前操作的元素會放到連結串列末尾
public static void main(String[] args) {
LinkedHashMap<String, String> lhm = new LinkedHashMap<String, String>(16, 0.75f, true);
lhm.put("1", "1");
lhm.put("2", "2");
lhm.put("3", "3");
lhm.put("4", "4");
loopHashMap(lhm);
lhm.get("1");
loopHashMap(lhm);
lhm.get("4");
loopHashMap(lhm);
}
輸出結果
第一次遍歷,最後放的元素是4,元素4會放到連結串列末尾
第二次遍歷,先操作元素1,元素1會放到連結串列末尾
第三次遍歷,操作元素4,元素4會放到連結串列末尾
1234
2341
2314
驗證accessOrder為false時候,LinkedHashMap是按照元素的新增順序遍歷
public static void main(String[] args) {
LinkedHashMap<String, String> lhm = new LinkedHashMap<String, String>(16, 0.75f, false);
lhm.put("1", "1");
lhm.put("2", "2");
lhm.put("3", "3");
lhm.put("4", "4");
loopHashMap(lhm);
lhm.get("1");
loopHashMap(lhm);
lhm.get("4");
loopHashMap(lhm);
}
輸出
1234
1234
1234
總結:accessOrder為true,按照元素的訪問順序,get(key)方法會改變順序
accessOrder為false,按照元素的新增順序,get(key)方法不會改變順序
參考文章:http://www.cnblogs.com/xrq730/p/5052323.html
相關文章
- LinkedHashMap 原始碼解讀(JDK 1.8)HashMap原始碼JDK
- LinkedHashMap,原始碼解讀就是這麼簡單HashMap原始碼
- LinkedHashMap原始碼詳解HashMap原始碼
- LinkedHashMap 詳解及原始碼簡析HashMap原始碼
- Java——LinkedHashMap原始碼解析JavaHashMap原始碼
- 搞懂 Java LinkedHashMap 原始碼JavaHashMap原始碼
- 【Java集合原始碼剖析】LinkedHashmap原始碼剖析Java原始碼HashMap
- JDK原始碼分析(四)——LinkedHashMapJDK原始碼HashMap
- jdk原始碼分析之LinkedHashMapJDK原始碼HashMap
- java基礎:LinkedHashMap — 原始碼分析JavaHashMap原始碼
- PostgreSQL 原始碼解讀(3)- 如何閱讀原始碼SQL原始碼
- JDK1.8 原始碼分析(九)--LinkedHashMapJDK原始碼HashMap
- Java類集框架 —— LinkedHashMap原始碼分析Java框架HashMap原始碼
- WeakHashMap,原始碼解讀HashMap原始碼
- Handler原始碼解讀原始碼
- Laravel 原始碼解讀Laravel原始碼
- Swoft 原始碼解讀原始碼
- SDWebImage原始碼解讀Web原始碼
- MJExtension原始碼解讀原始碼
- Masonry原始碼解讀原始碼
- HashMap原始碼解讀HashMap原始碼
- Redux原始碼解讀Redux原始碼
- require() 原始碼解讀UI原始碼
- ZooKeeper原始碼解讀原始碼
- FairyGUI原始碼解讀AIGUI原始碼
- 【C++】【原始碼解讀】std::is_same函式原始碼解讀C++原始碼函式
- 死磕 java集合之LinkedHashMap原始碼分析JavaHashMap原始碼
- LinkedHashMap就這麼簡單【原始碼剖析】HashMap原始碼
- vuex 原始碼:原始碼系列解讀總結Vue原始碼
- Laravel 原始碼的解讀Laravel原始碼
- reselect原始碼解讀原始碼
- ThreadLocal 原始碼解讀thread原始碼
- Redux原始碼完全解讀Redux原始碼
- Seajs原始碼解讀JS原始碼
- Axios 原始碼解讀iOS原始碼
- HashMap原始碼個人解讀HashMap原始碼
- Vue原始碼解讀一Vue原始碼
- Slim 框架原始碼解讀框架原始碼