HashMap原理分析(含1.8以后的紅黑樹)


一、概述

  HashMap這個類不管是Java開發還是Android開發都會經常用到,當有數據需要通過鍵值對的形式存儲的時候,使用Map會非常的方便。為什么要學習HashMap的原理呢?其中有兩點原因:

  1.通過對HashMap原理的學習,可以修煉開發者的內功,因為一旦理解的HashMap等於把數據結構都理解了(數組、鏈表、二叉樹)。ps:線性表又叫數組,紅黑樹又叫二叉樹

  2.第二個原因比較有意思,因為它是大廠高頻出現的一道面試題(你懂得)

二、數據結構

  數據結構是一個老生常談的問題,但是沒辦法,想要了解HashMap的原理這玩意根本繞不開,不要怕麻煩,一旦理解了數據結構HashMap的原理你一看就懂,而且看一遍基本上就會被了。

  jdk1.8以前的HashMap的數據結構=數組(數組)+鏈表

  jdk1.8及以后的HashMap的數據結構=數組(數組)+鏈表+紅黑樹(二叉樹)

  由於篇幅有限,下面簡單說一下線性表、鏈表、紅黑樹的數據結構的特性。

  1.數組:0個或多個數據元素的有限序列。

  它是一個順序存儲結構,你可以把它理解稱為一個數組。例如:存儲的第一個元素在數組下標為0的位置,第二個在數組下標為1的位置依次類推。由於線性表的這個順序存儲特性,導致了其查詢效率特別高,以為只需要拿到下標就可以獲取到數組下標對應的元素值。但是其插入和刪除的效率就不敢恭維了,由於是順序存儲導致了數組中不可能有空位置,如果再數組下標為0的位置刪除一個元素,那么數組中所有的元素都需要向前移動一位,其時間復雜度為(O(n))。插入也是同樣的道理,如果在數組中間位置插入一個元素,則插入位置之后的所有數組元素都需要向后移動一位,效率相對糟糕。

  優點:查詢效率很高

  缺點:插入和刪除效率低下

  2.鏈表:用一組任意存儲單元存儲線性表的數據元素,這組存儲單元可以是連續的也可以是不連續的。

  鏈表也是一種線性表,這是它里面的元素可以是連續的也可以是不連續的,這就意味這不用提前開辟一塊存儲容量,當鏈表中沒有元素時,不占用內存空間。數組中的元素必須是連續存儲的,而鏈表不必這么干,我們可以在任意的內存地址中存儲我們的鏈表元素,而這些內存地址還可以是分散的。這意味着我們在插入和刪除的時候不需要做任何的移動操作。這就大大提高了插入和刪除的效率。但是單鏈表的讀取卻是一個麻煩事,因為每次讀取都需要從頭開始查找,直到把元素找到為止,其時間復雜度為(O(n))。

  優點:插入和刪除效率高

  缺點:查詢效率低下

  3.二叉樹(紅黑樹):樹結構是一種一對多的數據結構。

  一棵樹中有一個根節點,根節點下又有子節點和葉子節點,子節點下又有子節點和葉子節點,依次往下推構成了一顆完整的樹。上面講到線性表的有序結構(數組)和線性表的鏈表結構,它們兩個都有其各自的優點和缺點,根據不同的使用場景來選取不同的數據結構。但是有沒有一種結構是綜合以上兩種結構的優點的呢?還真有,它就是現在正在講的二叉樹結構。

  有點:插入和刪除的效力都相對較高

  缺點:和數組、鏈表的優點相比,比較弱

  廢話不多說,下面分析一下HashMap的原理(純原理)

四、HashMap原理

  1.jdk1.8以前

  在jdk1.8以前HashMap的數據結構是數組+鏈表的形式。

  1.在HashMap創建的時候會初始化一個數組,這個數組的容量是16,用來存儲存放進去的元素。

  2.put(key,value)

    HashMap在添加元素時會根據key計算計算出一個hash值,用來確定元素在數組中的存放位置。這里面有兩種情況:1.如果計算出來的hash值在數組中的位置不存在元素,就直接把這個元素放入該位置,如果已經存在了元素(發生了碰撞),就以鏈表的形式把元素存儲在這個位置,新加入的元素在鏈表頭部,最先加入的放在鏈表的尾部。

  3.get(key)

  在數組中獲取元素的時候,現根據key計算出hash值,然后利用hash計算出元素在數組中的位置,然后遍歷鏈表利用key.equals方法取出對應的元素。

  2.jdk1.8以后

  在jdk1.8以及以后引入了紅黑樹數據結構,其HashMap的數據結構=數組+鏈表+紅黑樹

  1.在HashMap創建時依然會初始化一個數組大小,其大小為16.

  2.put(key,value)

    根據key計算出key的hash值,如果hash值對應的位置在數組中沒有對應的元素就直接存入,如果有就把新元素加入鏈表頭,最先加入的元素放到鏈表尾部。

  3.get(key)

    根據key計算出key的hash值,然后利用hash值查數組中對應位置的鏈表,中的元素,這里有一個前提條件。1.如果鏈表的長度小於8就直接從鏈表中查找,並取出。2.如果鏈表的長度大於等於8就把鏈表轉換為紅黑樹然后遍歷樹取出元素。

  

  面試中最容易問到的三個問題:

  1.HashMap擴容原理?

  當HashMap的元素越來越多的時候其發生碰撞的幾率也會越來越大(數組長度固定)。所以為了提高查詢效率就需要對HashMap進行擴容,但是擴容后會對性能有較大的影響,因為數組長度擴展以后會把原來的數組鏈表的元素都移動到新的數組中。

  HashMap的初始化容量是16,HashMap有一個加載因子(loadFactor)默認值為0.75。當HashMap中的元素個數超過HashMap容量*0.75的時候就會發生擴容。擴容后的數組大小為2*原始容量,即二倍。

  舉個栗子:原始容量是16,加載因子(loadFactor)是0.75。也就是說當HashMap中元素的個數>16*0.75=12的時候就會發生擴容。

  2.HashMap如何解決碰撞的?

    HashMap使用鏈表來解決碰撞沖突,當數組中的key的hash值位置上有元素是,就把這個元素放入鏈表的頭部,最先加入的放到鏈表的尾部。

  3.為什么要在jdk1.8以后再HashMap中引入紅黑樹?

    引入紅黑樹是為了提高HashMap的查詢效率。假設一種情況:當HashMap中的碰撞越來越多,鏈表越來越長的時候,其獲取單個元素所需要的時間就會越來越高(因為鏈表的查詢速度比較慢)。為了解決這個問題jdk1.8引入了紅黑樹。因為紅黑樹的查詢速度比鏈表要高很多。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM