HashMap,1.7與1.8的區別
底層數據結構的區別
JDK 1.8之前:
1)JDK1.8 之前HashMap 底層是數組和鏈表結合在一起使用也就是鏈表散列。
2)HashMap 通過key 的hashCode 經過擾動函數處理過后得到hash 值,然后通過(n - 1)& hash 判斷當前元素存放的位置(這里的n 指的是數組的長度),如果當前位置存在元素的話,就判斷該元素與要存入的元素的hash 值以及key 是否相同,如果相同的話,直接覆蓋,不相同就通過拉鏈法解決沖突。
3)所謂擾動函數指的就是HashMap 的hash 方法。使用hash 方法也就是擾動函數是為了防止一些實現比較差的hashCode() 方法換句話說使用擾動函數之后可以減少碰撞。
JDK 1.8之后:
當鏈表長度大于閾值(默認為 8)時,會首先調用 treeifyBin()方法。這個方法會根據HashMap 數組來決定是否轉換為紅黑樹。只有當數組長度大于或者等于64 的情況下,才會執行轉換紅黑樹操作,以減少搜索時間。否則,就是只是執行resize() 方法對數組擴容。
擴容機制的區別
一般情況下,當元素數量超過閾值時便會觸發擴容。每次擴容的容量都是之前容量的2 倍。HashMap 的容量是有上限的,必須小于1<<30,即1073741824。如果容量超出了這個數,則不再增長,且閾值會被設置為Integer.MAX_VALUE。
JDK7 中的擴容機制:
- 空參數的構造函數:以默認容量、默認負載因子、默認閾值初始化數組。內部數組是空數組。
- 有參構造函數:根據參數確定容量、負載因子、閾值等。
- 第一次put 時會初始化數組,其容量變為不小于指定容量的2 的冪數,然后根據負載因子確定閾值。
- 如果不是第一次擴容,則 新容量=舊容量x 2 ,新閾值=新容量x 負載因子。
JDK8 的擴容機制:
- 空參數的構造函數:實例化的HashMap 默認內部數組是null,即沒有實例化。第一次調用put 方法時,則會開始第一次初始化擴容,長度為16。
- 有參構造函數:用于指定容量。會根據指定的正整數找到不小于指定容量的2 的冪數,將這個數設置賦值給閾值(threshold)。第一次調用put 方法時,會將閾值賦值給容量, 然后讓 閾值= 容量x 負載因子。
- 如果不是第一次擴容,則容量變為原來的2 倍,閾值也變為原來的2 倍。(容量和閾值都變為原來的2 倍時,負載因子還是不變)。此外還有幾個細節需要注意:
- 首次put 時,先會觸發擴容(算是初始化),然后存入數據,然后判斷是否需要擴容;
- 不是首次put,則不再初始化,直接存入數據,然后判斷是否需要擴容;