Java總結之對映家族--Map概覽
所謂 對映
便是一一對應,map英語中是[地圖]的意思,這也很好的反應了對映的概念。
即:地圖上的某一點都會對應現實的某一點,說是對映可謂恰到好處。Map可以說是鍵值對的容器,key和value一一對應,也像是根據索引查詢單詞。
索引當然是不能重複的。如果你發現一個字典的索引有兩個[apple],你肯定會認為這個字典有問題。或者一個地圖上查詢兩個[合肥],恐怕你也不會相信這張地圖是好的。所以Map可作為Set的超集,Java中的Set集合的底層便是根據Map實現的。
Map家族一覽

Map介面.png
一、永恆閃耀的明星:HashMap
作為一個多考點的類,說起HashMap總是有種神聖不可侵犯的感覺。
相關話題:
雜湊碰撞相關問題:什麼是雜湊碰撞,如何降低雜湊碰撞機率,雜湊碰撞後的解決方案
HashMap底層實現問題:連結串列陣列+紅黑樹陣列,為什麼要使用這樣的資料結構
由此可以引出連結串列與陣列的比較:效率問題,空間問題,連結串列的實現
由此也引出紅黑樹的相關問題:什麼是紅黑樹,紅黑樹的特點,紅黑樹的翻轉,紅黑樹與AVL樹的比較

HashMap.png
來看一下HashMap的資料結構
這裡是Map總結篇,所以只是簡單的看一下,在HashMap精析中會詳細解釋
1---開啟原始碼,可以看出內部有一個Node類,而且是單鏈表
2---開啟原始碼,可以看出內部有一個TreeNode類,而且是紅黑樹
3---可以看到內部維護一個連結串列的陣列,當雜湊衝突時將資料插入連結串列尾
4---所以HashMap集陣列+單鏈表+紅黑樹於一身,對資料結構而言,確實很經典。
連結串列節點類:可見是一個單鏈表
static class Node<K,V> implements Map.Entry<K,V> { final int hash; final K key; V value; Node<K,V> next;
紅黑樹節點類
static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> { TreeNode<K,V> parent;// red-black tree links TreeNode<K,V> left; TreeNode<K,V> right; TreeNode<K,V> prev;// needed to unlink next upon deletion boolean red; TreeNode(int hash, K key, V val, Node<K,V> next) { super(hash, key, val, next); }
//可以看到內部維護一個數組連結串列 transient Node<K,V>[] table;
final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { //tab:記錄當前連結串列的陣列 //n:當前連結串列的陣列的長度 Node<K,V>[] tab; Node<K,V> p; int n, i; if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; if ((p = tab[i = (n - 1) & hash]) == null)//hash:傳入鍵的hash值 //可以看到新增元素並且沒有雜湊碰撞時,將元素放入陣列的下一位 tab[i] = newNode(hash, key, value, null); else {//否則,即雜湊衝突了 Node<K,V> e; K k; if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p; else if (p instanceof TreeNode)//如果是樹,按樹的插入 e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); else {//否則,按連結串列的插入, //關於bin:就像數組裡放了一個個垃圾桶(連結串列),來一個雜湊衝突的就往裡扔一個, //所以binCount就是連結串列的容量 for (int binCount = 0; ; ++binCount) { if ((e = p.next) == null) { //連結串列新增節點 p.next = newNode(hash, key, value, null); //數量達到樹化閥值 if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st treeifyBin(tab, hash);//樹化連結串列 break; } //省略... }
總結一下
1.HashMap會建立一個1 << 4(即16)的Node<K,V>(連結串列)陣列, 2.當hash衝突時,該元素會插入到與其衝突的連結串列尾 3.當連結串列長度為8並且陣列長度大於40時,連結串列轉為紅黑樹 4.當樹元素小於等於6時會解除樹化,分割成連結串列
為什麼連結串列要化為紅黑樹
單鏈表查詢、修改、移除、尾部新增元素的時間複雜度為O(n) 紅黑樹查詢、修改、移除、新增元素的時間複雜度為O(logn) 在資料量比較大師O(logn)的效率要比O(n)快很多(可根據數學上兩種曲線比較)
紅黑樹這麼好,為什麼不直接用紅黑樹?
1.考慮到雜湊衝突的資料不會是巨量的 2.在資料量比較少(樹化閥值為8)的時候O(n)和O(logn)並無不同 3.紅黑樹在插入和移除時會進行額外的旋轉操作,而且維護的成員變數較多邏輯較複雜,所以低資料量時反而不如單鏈表
二、鏈式雜湊對映:LinkedHashMap--我有序,我驕傲
LinkedHashMap<K,V>extends HashMap<K,V>
,so LinkedHashMap擁有HashMap功力
可見Entry是繼承自HashMap.Node(單鏈表)的, Entry<K,V> before, after,說明Entry是一個雙鏈表
由此解決了HashMap不能隨時保持遍歷順序和插入順序一致的問題,詳細原理會另開一篇
static class Entry<K,V> extends HashMap.Node<K,V> { Entry<K,V> before, after; Entry(int hash, K key, V value, Node<K,V> next) { super(hash, key, value, next); } }

LinkedHashMap.png
三、樹形對映:TreeMap--我是紅黑樹
1----基於紅黑樹,由於紅黑樹是一種特殊的二分搜尋樹,所以可保證鍵的有序性,也可自定義排序規則
2----containsKey、get、put 和 remove 操作時間複雜度結尾O(logn)
可見節點為紅黑樹
static final class Entry<K,V> implements Map.Entry<K,V> { K key; V value; Entry<K,V> left; Entry<K,V> right; Entry<K,V> parent; boolean color = BLACK;

TreeMap.png
四、雜湊表:Hashtable--就讓我成為歷史,躲在JDK的漆黑角落。但面試官總愛挖墳...
Hashtable的老爹Dictionary這樣介紹自己:"NOTE: This class is obsolete"
底層陣列+連結串列實現,無論key還是value都不能為null, 執行緒安全,實現執行緒安全的方式是在修改資料時鎖住整個HashTable,效率低 初始size為11,擴容:newsize = olesize*2+1 計算index的方法:index = (hash & 0x7FFFFFFF) % tab.length

Hashtable.png
五、併發雜湊對映:ConcurrentHashMap--我為併發而生
不是一兩句話能說清的
ConcurrentHashMap鎖的粒度,為對每個陣列元素(Node),Hashtable鎖的粒度,為對整個陣列

ConcurrentHashMap.png
最後總的比較一下
專案 | 執行緒安全? | 實現 | 擴容方式 | null鍵值 | 父類 |
---|---|---|---|---|---|
HashMap | 否 | 陣列+單鏈表+紅黑樹 | oldCap * 2 | 允許 | AbstractMap |
Hashtable | 是 | 陣列 + 連結串列 | oldCap * 2 + 1 | 不允許 | Dictionary |
ConcurrentHashMap | 是 | 陣列+單鏈表+紅黑樹 | oldCap * 2 | 允許 | AbstractMap |
LinkedHashMap | 否 | 陣列+單鏈表+紅黑樹+雙鏈表 | oldCap * 2 | 允許 | HashMap |
TreeMap | 否 | 紅黑樹 | ---- | 允許 | AbstractMap |
後記:捷文規範
1.本文成長記錄及勘誤表
專案原始碼 | 日期 | 備註 |
---|---|---|
V0.1--無 | 2018-10-1 | ofollow,noindex">Java總結之對映家族--Map概覽 |
V0.2--無 | - | - |
2.更多關於我
筆名 | 微信 | 愛好 | |
---|---|---|---|
張風捷特烈 | 1981462002 | zdl1994328 | 語言 |
我的github | 我的簡書 | 我的CSDN | 個人網站 |
3.宣告
1----本文由張風捷特烈原創,轉載請註明
2----歡迎廣大程式設計愛好者共同交流
3---個人能力有限,如有不正之處歡迎大家批評指證,必定虛心改正
4----看到這裡,我在此感謝你的喜歡與支援