日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

Java8 LinkedHashMap 源码阅读

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

如果你對 HashMap 的源碼有了解的話,只需要一圖就能知道 LinkedHashMap 的原理了,但是具體的實現(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é)點,用于維護(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é)點是怎么維護(hù)插入順序的呢?在 LinkedHashMap 中是沒有 TreeNode 類的,因為它復(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 屬性又有什么作用呢,下面來解釋一下,看了解釋大家可能還會有點迷惑,后面結(jié)合代碼大家就能理解這個字段的作用了。

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

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

二、添加鍵值對

上面我們也說了 LinkedHashMap 中沒有 put 方法,因為都是復(fù)用的 HashMap 的,如果你想具體了解 put 方法,可以查看 HashMap 擴容機制與線程安全分析 這篇文章。

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

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ào)用了一個 linkNodeLast 方法,我想看到這里大家應(yīng)該這道其中的原理了。

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

三、獲取鍵值對

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

public V get(Object key) {Node<K,V> e;// 調(diào)用 hashMap 中的 getNode() 方法,根據(jù) key 的哈希值找到對應(yīng)的桶位置,判斷節(jié)點后(鏈表、頭結(jié)點、樹節(jié)點)進(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é)點進(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é)點直接返回if (first.hash == hash && // always check first node((k = first.key) == key || (key != null && key.equals(k))))return first;// 如果頭節(jié)點不是要找的節(jié)點,判斷是樹節(jié)點還是鏈表節(jié)點后繼續(xù)查找if ((e = first.next) != null) {if (first instanceof TreeNode)return ((TreeNode<K,V>)first).getTreeNode(hash, key);// 鏈表、從頭節(jié)點開始遍歷查找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é)點。

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

四、HashMap 與 LinkedHashMap 對比

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

4.1 containsValue 方法對比

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

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é)點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é)點遍歷,一直遍歷到尾節(jié)點。

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é)點。

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é)點、樹節(jié)點或鏈表)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é)點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)容還不錯,歡迎將生活随笔推薦給好友。