Java8 HashMap详解
文章推薦
- 精選java等全套學習資源
- 精選java電子圖書資源
- 精選大數據學習資源
- java項目練習精選
Java8 對 HashMap 進行了一些修改,最大的不同就是利用了紅黑樹,所以其由 數組+鏈表+紅黑樹 組成。
根據 Java7 HashMap 的介紹,我們知道,查找的時候,根據 hash 值我們能夠快速定位到數組的具體下標,但是之后的話,需要順著鏈表一個個比較下去才能找到我們需要的,時間復雜度取決于鏈表的長度,為 O(n)。
為了降低這部分的開銷,在 Java8 中,當鏈表中的元素超過了 8 個以后,會將鏈表轉換為紅黑樹,在這些位置進行查找的時候可以降低時間復雜度為 O(logN)。
來一張圖簡單示意一下吧:
注意,上圖是示意圖,主要是描述結構,不會達到這個狀態的,因為這么多數據的時候早就擴容了。
下面,我們還是用代碼來介紹吧,個人感覺,Java8 的源碼可讀性要差一些,不過精簡一些。
Java7 中使用 Entry 來代表每個 HashMap 中的數據節點,Java8 中使用 Node,基本沒有區別,都是 key,value,hash 和 next 這四個屬性,不過,Node 只能用于鏈表的情況,紅黑樹的情況需要使用 TreeNode。
我們根據數組元素中,第一個節點數據類型是 Node 還是 TreeNode 來判斷該位置下是鏈表還是紅黑樹的。
##put 過程分析
和 Java7 稍微有點不一樣的地方就是,Java7 是先擴容后插入新值的,Java8 先插值再擴容,不過這個不重要。
##數組擴容
resize() 方法用于初始化數組或數組擴容,每次擴容后,容量為原來的 2 倍,并進行數據遷移。
final Node<K,V>[] resize() {Node<K,V>[] oldTab = table;int oldCap = (oldTab == null) ? 0 : oldTab.length;int oldThr = threshold;int newCap, newThr = 0;if (oldCap > 0) { // 對應數組擴容if (oldCap >= MAXIMUM_CAPACITY) {threshold = Integer.MAX_VALUE;return oldTab;}// 將數組大小擴大一倍else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&oldCap >= DEFAULT_INITIAL_CAPACITY)// 將閾值擴大一倍newThr = oldThr << 1; // double threshold}else if (oldThr > 0) // 對應使用 new HashMap(int initialCapacity) 初始化后,第一次 put 的時候newCap = oldThr;else {// 對應使用 new HashMap() 初始化后,第一次 put 的時候newCap = DEFAULT_INITIAL_CAPACITY;newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);}if (newThr == 0) {float ft = (float)newCap * loadFactor;newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?(int)ft : Integer.MAX_VALUE);}threshold = newThr;// 用新的數組大小初始化新的數組Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];table = newTab; // 如果是初始化數組,到這里就結束了,返回 newTab 即可if (oldTab != null) {// 開始遍歷原數組,進行數據遷移。for (int j = 0; j < oldCap; ++j) {Node<K,V> e;if ((e = oldTab[j]) != null) {oldTab[j] = null;// 如果該數組位置上只有單個元素,那就簡單了,簡單遷移這個元素就可以了if (e.next == null)newTab[e.hash & (newCap - 1)] = e;// 如果是紅黑樹,具體我們就不展開了else if (e instanceof TreeNode)((TreeNode<K,V>)e).split(this, newTab, j, oldCap);else { // 這塊是處理鏈表的情況,// 需要將此鏈表拆成兩個鏈表,放到新的數組中,并且保留原來的先后順序// loHead、loTail 對應一條鏈表,hiHead、hiTail 對應另一條鏈表,代碼還是比較簡單的Node<K,V> loHead = null, loTail = null;Node<K,V> hiHead = null, hiTail = null;Node<K,V> next;do {next = e.next;if ((e.hash & oldCap) == 0) {if (loTail == null)loHead = e;elseloTail.next = e;loTail = e;}else {if (hiTail == null)hiHead = e;elsehiTail.next = e;hiTail = e;}} while ((e = next) != null);if (loTail != null) {loTail.next = null;// 第一條鏈表newTab[j] = loHead;}if (hiTail != null) {hiTail.next = null;// 第二條鏈表的新的位置是 j + oldCap,這個很好理解newTab[j + oldCap] = hiHead;}}}}}return newTab; }##get 過程分析
相對于 put 來說,get 真的太簡單了。
- 計算 key 的 hash 值,根據 hash 值找到對應數組下標: hash & (length-1)
- 判斷數組該位置處的元素是否剛好就是我們要找的,如果不是,走第三步
- 判斷該元素類型是否是 TreeNode,如果是,用紅黑樹的方法取數據,如果不是,走第四步
- 遍歷鏈表,直到找到相等(==或equals)的 key
出處:https://www.javadoop.com/post/hashmap#Java7%20ConcurrentHashMap
總結
以上是生活随笔為你收集整理的Java8 HashMap详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java7 ConcurrentHash
- 下一篇: Java8 ConcurrentHash