HashMap 和 TreeMap 的內部結構

一、HashMap

1、基於哈希表的 Map 接口的實現。

此實現提供所有可選的映射操作,並允許使用 null 值和 null 鍵。(除了非同步和允許使用 null 之外,HashMap 類與 Hashtable 大致相同。)此類不保證映射的順序,特別是它不保證該順序恆久不變。

2、HashMap 的實例有兩個參數影響其性能:初始容量 和 加載因子

容量是哈希表中桶的數量,初始容量只是哈希表在創建時的容量。

加載因子是哈希表在其容量自動增加之前可以達到多滿的一種尺度。當哈希表中的條目數超出了加載因子與當前容量的乘積時,則要對該哈希表進行rehash 操作(即重建內部數據結構),從而哈希表將具有大約兩倍的桶數。

按照key關鍵字的哈希值和buckets數組的長度取模查找桶的位置,如果key的哈希值相同,Hash衝突(也就是指向了同一個桶)則每次新添加的作爲頭節點,而最先添加的在表尾。

HashMap中的桶的個數就是下圖中的0- n的數組的長度,存儲第一個entry的位置叫桶(bucket)而桶中只能存一個值也就是鏈表的頭節點,鏈表的每個節點就是添加的一個值(HashMap內部類Entry的實例Entry有哪些屬性之後在詳說)。

也可以這樣理解,一個entry 類型的存儲鏈表的數組。數組的索引位置就是一個個桶的索引地址。

從上圖我們可以發現哈希表是由數組+鏈表組成的,一個長度爲16的數組中,每個元素存儲的是一個鏈表的頭結點。那麼這些元素是按照什麼樣的規則存儲到數組中呢。

一般情況是通過hash(key)%len獲得,也就是元素的key的哈希值對數組長度取模得到。比如上述哈希表中,12%16=12、28%16=12、108%16=12、140%16=12。所以12、28、108以及140都存儲在數組下標爲12的位置。

HashMap簡單總結:

1、HashMap 是鏈式數組(存儲鏈表的數組)實現查詢速度可以,而且能快速的獲取key對應的value;

2、查詢速度的影響因素有 容量和負載因子,容量大負載因子小查詢速度快但浪費空間,反之則相反;

3、數組的index值是(key 關鍵字, hashcode爲key的哈希值, len 數組的大小):hashcode%len的值來確定,如果容量大負載因子小則index相同(index相同也就是指向了同一個桶)的概率小,鏈表長度小則查詢速度快,反之index相同的概率大鏈表比較長查詢速度慢。

4、對於HashMap以及其子類來說,他們是採用hash算法來決定集合中元素的存儲位置,當初始化HashMap的時候系統會創建一個長度爲capacity的Entry數組,這個數組裏可以存儲元素的位置稱爲桶(bucket),每一個桶都有其指定索引,系統可以根據索引快速訪問該桶中存儲的元素。

5、無論何時HashMap 中的每個桶都只存儲一個元素(Entry 對象)。由於Entry對象可以包含一個引用變量用於指向下一個Entry,因此可能出現HashMap 的桶(bucket)中只有一個Entry,但這個Entry指向另一個Entry 這樣就形成了一個Entry 鏈。

6、通過上面的源碼發現HashMap在底層將key_value對當成一個整體進行處理(Entry 對象)這個整體就是一個Entry對象,當系統決定存儲HashMap中的key_value對時,完全沒有考慮Entry中的value,而僅僅是根據key的hash值來決定每個Entry的存儲位置。

注意點

JDK1.8中使用一個Node數組來存儲數據,但這個Node可能是鏈表結構,也可能是紅黑樹結構如果插入的key的hashcode相同,那麼這些key也會被定位到Node數組的同一個格子裏。

如果同一個格子裏的key不超過8個,使用鏈表結構存儲。如果超過了8個,那麼會調用treeifyBin函數,將鏈表轉換爲紅黑樹。那麼即使hashcode完全相同,由於紅黑樹的特點,查找某個特定元素,也只需要O(log n)的開銷。

也就是說put/get的操作的時間複雜度最差只有O(log n)。

需要注意:key的對象,必須正確的實現了Compare接口

二、TreeMap

1、紅黑樹是一種近似平衡的二叉查找樹,它能夠確保任何一個節點的左右子樹的高度差不會超過二者中較低那個的一倍。具體來說,紅黑樹是滿足如下條件的二叉查找樹(binary search tree):

  1. 每個節點要麼是紅色,要麼是黑色。

  2. 根節點必須是黑色

  3. 紅色節點不能連續(也即是,紅色節點的孩子和父親都不能是紅色)。

  4. 對於每個節點,從該點至null(樹尾端)的任何路徑,都含有相同個數的黑色節點。

在樹的結構發生改變時(插入或者刪除操作),往往會破壞上述條件3或條件4,需要通過調整使得查找樹重新滿足紅黑樹的條件。

2、TreeMap的底層使用了紅黑樹來實現,像TreeMap對象中放入一個key-value 鍵值對時,就會生成一個Entry對象,這個對象就是紅黑樹的一個節點,其實這個和HashMap是一樣的,一個Entry對象作爲一個節點,只是這些節點存放的方式不同。

3、存放每一個Entry對象時都會按照key鍵的大小按照二叉樹的規範進行存放,所以TreeMap中的數據是按照key從小到大排序的。

TreeMap總結:

程序添加新節點時,總是從樹的根節點開始比較,即將根節點當成當前節點。如果新增節點大於當前節點並且當前節點的右節點存在,則以右節點作爲當前節點,如果新增節點小於當前節點並且當前節點的左子節點存在,則以左子節點作爲當前節點;

如果新增節點等於當前節點,則用新增節點覆蓋當前節點,並結束循環 直到某個節點的左右子節點不存在,將新節點添加爲該節點的子節點。如果新節點比該節點大,則添加其爲右子節點。如果新節點比該節點小,則添加其爲左子節點。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章