HashMap 可以允許key為null,value為null,但HashMap的是線程不安全的 HashMap 底層是數組 + 鏈表的數據結構
- 在jdk 1.7 中 map集合中的每一項都是一個 entry
- 在jdk 1.8 中 map 集合中的每一項都是一個node
這張圖我解釋一下 在每個HashMap中 維護了四個屬性 分別是 hash ,map ,key ,next
因為底層是數組加鏈表 數組的默認大小是16 每一個 用戶put值的時候都會向這個數組進行添加 這里的添加算法就是 hash算法,一旦用戶的數據向這個數組的某一項進行第二次輔助則這時就會使用鏈表的數據結構 一直向下延申 這個是jdk1.7 的做法
而到了jdk1.8以后 在延申的過程中一旦發現 延申的數量大於8個就會使用另一種的數據結構 紅黑樹以樹的形式進行擴展進行添加
這時肯定會有人問剛剛一直往下延申這時 的橫向延申不夠怎么辦 就是數組的長度不足怎么辦 這時數組當然也是會擴容 以原理的二倍進行擴容相對來說效率比較高
HashMap 數組+鏈表+紅黑樹 在單線程的情況下簡直是完美的
但在多線程的情況下是不完美的 會導致線程不安全
線程不安全 :
多線程操作一系列操作的時候和單線程操作表現的結果不致就說明線程非安全在 hashmap1.7中頭插法hsah map是線程不安全的
這時我們的Hashtable 出現了
- hashtable不接受key 或者 value為空
- 線程安全
但是 是線程安全的 但在高並發 或者負載均衡的輪詢等 效率太 低
ConcurrentHashMap
采用的是分段鎖
在並發的概念中 有一個編程思想是CAS方式 采用無鎖的方式 保證線程的安全 保證了原子性 效率比 synchronized 高
接下來我們對比一下HashMap 和 ConcurrentHashMap的put 方式
Hashmap 的put方法
ConcurrentHashMap的put 方法
點入到ConcurrentHashMap的put 方法
這時我們在ConcurrentHashMap集合中看到 好多volatile關鍵字
volatile
這個關鍵字 保證每次讀到的都是主內存里面最新的值
- 擴容:段內擴容(段內元素超過該段對應Entry數組長度的75%觸發擴容,不會對整個Map進行擴容),插入前檢測需不需要擴容,有效避免無效擴容
- ConcurrentHashMap默認將hash表分為16個桶,諸如get、put、remove等常用操作只鎖住當前需要用到的桶。這樣,原來只能一個線程進入,現在卻能同時有16個寫線程執行,並發性能的提升是顯而易見的。