日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

Java8 LinkedHashMap 源码阅读

發(fā)布時間:2024/9/30 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java8 LinkedHashMap 源码阅读 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

如果你對 HashMap 的源碼有了解的話,只需要一圖就能知道 LinkedHashMap 的原理了,但是具體的實(shí)現(xiàn)細(xì)節(jié)還是需要去讀一下源碼。

一、LinkedHashMap 簡介

1.1 繼承結(jié)構(gòu)

從繼承結(jié)構(gòu)上來講 LinkedHashMap 繼承自 HashMap,LinkedHashMap 中沒有提供任何增刪改查的方法,而是直接復(fù)用了父類 HashMap 中的方法。

1.2 內(nèi)部數(shù)據(jù)結(jié)構(gòu)

LinkedHashMap 繼承自 HashMap,HashMap 底層存儲鍵值對的數(shù)據(jù)結(jié)構(gòu)是 Node 和 TreeNode,而 LinkedHashMap 存儲鍵值對的數(shù)據(jù)結(jié)構(gòu)是 Entry 和 TreeNode。

// Entry 繼承自 HashMap 中的 Node 類,用于鏈表存儲鍵值對static class Entry<K,V> extends HashMap.Node<K,V> {// 前置與后置節(jié)點(diǎn),用于維護(hù) LinkedHashMap 的鍵值對順序Entry<K,V> before, after;Entry(int hash, K key, V value, Node<K,V> next) {super(hash, key, value, next);}}

Entry 用于存儲鏈表中的鍵值對,用 before 與 next 指針維護(hù)插入鍵值對的順序,那樹節(jié)點(diǎn)是怎么維護(hù)插入順序的呢?在 LinkedHashMap 中是沒有 TreeNode 類的,因?yàn)樗鼜?fù)用了 HashMap 中的 TreeNode,下面我們來看一下 HashMap 中 TreeNode 的定義。

我們只看這一行代碼就行了,你會發(fā)現(xiàn) HashMap 中 TreeNode 類繼承了 LinkedHashMap 的 Entry,這樣一來就繼承了父類的前置與后置指針,也就能維護(hù) LinkedHashMap 的插入順序了。

static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V>

1.3 構(gòu)造函數(shù)

我們只看其中一個構(gòu)造函數(shù)

public LinkedHashMap(int initialCapacity,float loadFactor,boolean accessOrder) {// 調(diào)用 HashMap 的構(gòu)造函數(shù)super(initialCapacity, loadFactor);this.accessOrder = accessOrder;}

LinkedHashMap 只有這一個構(gòu)造函數(shù)可以初始化 accessOrder,這個 accessOrder 屬性又有什么作用呢,下面來解釋一下,看了解釋大家可能還會有點(diǎn)迷惑,后面結(jié)合代碼大家就能理解這個字段的作用了。

/** 鍵值對迭代順序策略* true:access-order 訪問順序,使用迭代器遍歷時,get 的元素會被添加到最后* false:insertion-order 插入順序*/final boolean accessOrder;

上面講了那么多都是為相關(guān)源碼分析作準(zhǔn)備,下面我們就一起來看看吧。

二、添加鍵值對

上面我們也說了 LinkedHashMap 中沒有 put 方法,因?yàn)槎际菑?fù)用的 HashMap 的,如果你想具體了解 put 方法,可以查看 HashMap 擴(kuò)容機(jī)制與線程安全分析 這篇文章。

想要把鍵值對放到 LinkedHashMap 中去,一定會先封裝成 Entry 或 TreeNode 節(jié)點(diǎn),知道這個原理我們直接來看 newNode(以鏈表節(jié)點(diǎn)為例) 方法。

Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {// 初始化 LinkedHashMap 鍵值對 EntryLinkedHashMap.Entry<K,V> p =new LinkedHashMap.Entry<K,V>(hash, key, value, e);// 添加的鍵值對都會被添加到 LinkedHashMap 尾部,因此可以形成一個雙向鏈表linkNodeLast(p);return p;}

在 newNode 方法初始化 Entry 節(jié)點(diǎn)時調(diào)用了一個 linkNodeLast 方法,我想看到這里大家應(yīng)該這道其中的原理了。

private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {// 獲取并記錄 tail 節(jié)點(diǎn)LinkedHashMap.Entry<K,V> last = tail;// 重置 tailtail = p;if (last == null)head = p;else {// 將節(jié)點(diǎn)連接起來,構(gòu)成雙向鏈表p.before = last;last.after = p;}}

三、獲取鍵值對

LinkedHashMap 的 get 方法調(diào)用了 HashMap 的 getNode 方法,獲取到對應(yīng)的節(jié)點(diǎn)后,返回對應(yīng)的 value。

public V get(Object key) {Node<K,V> e;// 調(diào)用 hashMap 中的 getNode() 方法,根據(jù) key 的哈希值找到對應(yīng)的桶位置,判斷節(jié)點(diǎn)后(鏈表、頭結(jié)點(diǎn)、樹節(jié)點(diǎn))進(jìn)行返回if ((e = getNode(hash(key), key)) == null)return null;// 如果 accessOrder 為 true,獲取元素后把當(dāng)前鍵值對調(diào)整到尾部if (accessOrder)afterNodeAccess(e);return e.value;}

下面我們來看一下 getNode 方法都做了什么。這個方法做的事情很簡答, 根據(jù) key 計算出對應(yīng)的哈希值,根據(jù)哈希值計算出對應(yīng)的桶位置,然后遍歷節(jié)點(diǎn)進(jìn)行查找。

final Node<K,V> getNode(int hash, Object key) {Node<K,V>[] tab; Node<K,V> first, e; int n; K k;// 進(jìn)行判斷并通過 tab[(n - 1) & hash] 計算當(dāng)前 key 在哈希表中的位置if ((tab = table) != null && (n = tab.length) > 0 &&(first = tab[(n - 1) & hash]) != null) {// 如果是當(dāng)前桶位置上的頭節(jié)點(diǎn)直接返回if (first.hash == hash && // always check first node((k = first.key) == key || (key != null && key.equals(k))))return first;// 如果頭節(jié)點(diǎn)不是要找的節(jié)點(diǎn),判斷是樹節(jié)點(diǎn)還是鏈表節(jié)點(diǎn)后繼續(xù)查找if ((e = first.next) != null) {if (first instanceof TreeNode)return ((TreeNode<K,V>)first).getTreeNode(hash, key);// 鏈表、從頭節(jié)點(diǎn)開始遍歷查找do {if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))return e;} while ((e = e.next) != null);}}return null;}

上面的 get 方法中還有一個需要注意的地方,如果 accessOrdera 為 true 則調(diào)用 fterNodeAccess(e); 方法,這個方法又是干嘛用的呢,在最開始的時候我們說過,LinkedHashMap 支持兩種迭代策略,分別是訪問順序(默認(rèn))與插入順序,這個 fterNodeAccess(e); 方法就是用于支持訪問順序的。

如果 accessOrdera 為 true,那么在調(diào)用 get 方法時會將該鍵值對置為雙向鏈表的尾節(jié)點(diǎn)。

void afterNodeAccess(Node<K,V> e) { // move node to lastLinkedHashMap.Entry<K,V> last;// 判斷迭代策略,并且當(dāng)前節(jié)點(diǎn)不是尾節(jié)點(diǎn)if (accessOrder && (last = tail) != e) {// 記錄當(dāng)前節(jié)點(diǎn),并獲取前后節(jié)點(diǎn)LinkedHashMap.Entry<K,V> p =(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;// 把當(dāng)前節(jié)點(diǎn)的 after 節(jié)點(diǎn)置 nullp.after = null;// 如果當(dāng)前節(jié)點(diǎn)是頭節(jié)點(diǎn),把后一個節(jié)點(diǎn)置為頭節(jié)點(diǎn)if (b == null)head = a;// 把當(dāng)前節(jié)點(diǎn)的前后節(jié)點(diǎn)相連elseb.after = a;if (a != null)a.before = b;elselast = b;if (last == null)head = p;// 把當(dāng)前節(jié)點(diǎn)置為尾節(jié)點(diǎn)并記錄else {p.before = last;last.after = p;}tail = p;++modCount;}}

四、HashMap 與 LinkedHashMap 對比

除了雙向鏈表外,HashMap 與 LinkedHashMap 還有什么區(qū)別嗎?

4.1 containsValue 方法對比

HashMap 中代碼如下,遍歷哈希表中的所有桶,然后遍歷桶位置上的所有節(jié)點(diǎn)。

public boolean containsValue(Object value) {Node<K,V>[] tab; V v;if ((tab = table) != null && size > 0) {// 遍歷哈希表for (int i = 0; i < tab.length; ++i) {// 遍歷當(dāng)前桶上的所有節(jié)點(diǎn)for (Node<K,V> e = tab[i]; e != null; e = e.next) {if ((v = e.value) == value ||(value != null && value.equals(v)))return true;}}}return false;}

LinkedHashMap 中代碼如下,從頭節(jié)點(diǎn)遍歷,一直遍歷到尾節(jié)點(diǎn)。

public boolean containsValue(Object value) {/*** 遍歷雙向鏈表,判斷 value,與 HashMap 完全不同*/for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) {V v = e.value;if (v == value || (value != null && value.equals(v)))return true;}return false;}

4.2 nextNode 方法對比

HashMap 中的 nextNode() 方法,遍歷哈希表數(shù)組,接著遍歷桶位置上的所有節(jié)點(diǎn)。

final Node<K,V> nextNode() {// 記錄哈希表數(shù)組Node<K,V>[] t;Node<K,V> e = next;if (modCount != expectedModCount)throw new ConcurrentModificationException();if (e == null)throw new NoSuchElementException();// 過濾掉沒有鍵值對的桶位置if ((next = (current = e).next) == null && (t = table) != null) {do {} while (index < t.length && (next = t[index++]) == null);}// 下一個有鍵值對的桶(單個節(jié)點(diǎn)、樹節(jié)點(diǎn)或鏈表)return e;}

LinkedHashMap 中的 nextNode() 方法,根據(jù)雙向鏈表的順序迭代鍵值對。

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 為雙向鏈表的下一個節(jié)點(diǎn)next = e.after;return e;}

jdk1.8 源碼閱讀:https://github.com/zchen96/jdk1.8-source-code-read

參考資料

搞懂 Java LinkedHashMap 源碼

總結(jié)

以上是生活随笔為你收集整理的Java8 LinkedHashMap 源码阅读的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。