前言
聲明,本文用得是jdk1.8
前面已經講了Collection的總覽和剖析List集合:
原本我是打算繼續將Collection下的Set集合的,結果看了源碼發現:Set集合實際上就是HashMap來構建的!
所以,就先介紹Map集合、散列表和紅黑樹吧!
看這篇文章之前最好是有點數據結構的基礎:
當然了,如果講得有錯的地方還請大家多多包涵並不吝在評論去指正~
一、Map介紹
1.1為什么需要Map
前面我們學習的Collection叫做集合,它可以快速查找現有的元素。
而Map在《Core Java》中稱之為-->映射..
映射的模型圖是這樣的:
那為什么我們需要這種數據存儲結構呢???舉個例子
- 作為學生來說,我們是根據學號來區分不同的學生。只要我們知道學號,就可以獲取對應的學生信息。這就是Map映射的作用!
生活中還有很多這樣的例子:只要你掏出身份證(key),那就可以證明是你自己(value)
1.2Map與Collection的區別
1.3Map的功能
下面我們來看看Map的源碼:
簡單常用的Map功能有這么一些:
下面用紅色框框圈住的就是Map值得關注的子類:
二、散列表介紹
無論是Set還是Map,我們會發現都會有對應的-->HashSet,HashMap
首先我們也先得回顧一下數據和鏈表:
- 鏈表和數組都可以按照人們的意願來排列元素的次序,他們可以說是有序的(存儲的順序和取出的順序是一致的)
- 但同時,這會帶來缺點:想要獲取某個元素,就要訪問所有的元素,直到找到為止。
- 這會讓我們消耗很多的時間在里邊,遍歷訪問元素~
而還有另外的一些存儲結構:不在意元素的順序,能夠快速的查找元素的數據
- 其中就有一種非常常見的:散列表
2.1散列表工作原理
散列表為每個對象計算出一個整數,稱為散列碼。根據這些計算出來的整數(散列碼)保存在對應的位置上!
在Java中,散列表用的是鏈表數組實現的,每個列表稱之為桶。【之前也寫過桶排序就這么簡單,可以回顧回顧】
一個桶上可能會遇到被占用的情況(hashCode散列碼相同,就存儲在同一個位置上),這種情況是無法避免的,這種現象稱之為:散列沖突
- 此時需要用該對象與桶上的對象進行比較,看看該對象是否存在桶子上了~如果存在,就不添加了,如果不存在則添加到桶子上
- 當然了,如果hashcode函數設計得足夠好,桶的數目也足夠,這種比較是很少的~
- 在JDK1.8中,桶滿時會從鏈表變成平衡二叉樹
如果散列表太滿,是需要對散列表再散列,創建一個桶數更多的散列表,並將原有的元素插入到新表中,丟棄原來的表~
- 裝填因子(load factor)決定了何時對散列表再散列~
- 裝填因子默認為0.75,如果表中超過了75%的位置已經填入了元素,那么這個表就會用雙倍的桶數自動進行再散列
當然了, 在后面閱讀源碼的時候會繼續說明的,現在簡單了解一下即可~
擴展閱讀:
三、紅黑樹介紹
上面散列表中已經提過了:如果桶數滿的時候,JDK8是將鏈表轉成紅黑樹的~。並且,我們的TreeSet、TreeMap底層都是紅黑樹來實現的。
所以,在這里學習一波紅黑樹到底是啥玩意。
之前涉及過二叉樹的文章:
在未學習之前,我們可能是聽過紅黑樹這么一個數據結構類型的,還有其他什么B/B+樹等等,反正是比較復雜的數據結構了~~~
各種常見的樹的用途:
來源:
https://www.zhihu.com/question/30527705/answer/52527887
3.1回顧二叉查找樹
首先我們來回顧一下:利用二叉查找樹的特性,我們一般來說可以很快地查找出對應的元素。
可是二叉查找樹也有個例(最壞)的情況(線性):
上面符合二叉樹的特性,但是它是線性的,完全沒樹的用處~
樹是要“均衡”才能將它的優點展示出來的~,比如下面這種:
因此,就有了平衡樹這么一個概念~紅黑樹就是一種平衡樹,它可以保證二叉樹基本符合矮矮胖胖(均衡)的結構
3.2知新2-3樹
講到了平衡樹就不得不說最基礎的2-3樹,2-3樹長的是這個樣子:
在二叉查找樹上,我們插入節點的過程是這樣的:小於節點值往右繼續與左子節點比,大於則繼續與右子節點比,直到某節點左或右子節點為空,把值插入進去。這樣無法避免偏向問題
而2-3樹不一樣:它插入的時候可以保持樹的平衡!
在2-3樹插入的時可以簡單總結為兩個操作:
- 合並2-節點為3-節點,擴充將3-節點擴充為一個4-節點
- 分解4-節點為3-節點,節點3-節點為2-節點
- ........至使得樹平衡~
合並分解的操作還是比較復雜的,要分好幾種情況,代碼量很大這里我就不介紹了,因為要學起來是一大堆的,很麻煩
3.3從2-3樹到紅黑樹
由於2-3樹為了保持平衡性,在維護的時候是需要大量的節點交換的!這些變換在實際代碼中是很復雜的,大佬們在2-3樹的理論基礎上發明了紅黑樹(2-3-4樹也是同樣的道理,只是2-3樹是最簡單的一種情況,所以我就不說2-3-4樹了)。
- 紅黑樹是對2-3查找樹的改進,它能用一種統一的方式完成所有變換。
紅黑樹是一種平衡二叉樹,因此它沒有3-節點。那紅黑樹是怎么將3-節點來改進成全都是二叉樹呢?
紅黑樹就字面上的意思,有紅色的節點,有黑色的節點:
我們可以將紅色節點的左鏈接畫平看看:
一顆典型的二叉樹:
將紅色節點的左鏈接畫平之后:得到2-3平衡樹:
3.4紅黑樹基礎知識
前面已經說了,紅黑樹是在2-3的基礎上實現的一種樹,它能夠用統一的方式完成所有變換。很好理解:紅黑樹也是平衡樹的一種,在插入元素的時候它也得保持樹的平衡,那紅黑樹是以什么的方式來保持樹的平衡的呢?
紅黑樹用的是也是兩種方式來替代2-3樹不斷的節點交換操作:
- 旋轉:順時針旋轉和逆時針旋轉
- 反色:交換紅黑的顏色
- 這個兩個實現比2-3樹交換的節點(合並,分解)要方便一些
紅黑樹為了保持平衡,還有制定一些約束,遵守這些約束的才能叫做紅黑樹:
- 紅黑樹是二叉搜索樹。
- 根節點是黑色。
- 每個葉子節點都是黑色的空節點(NIL節點)。
- 每個紅色節點的兩個子節點都是黑色。(從每個葉子到根的所有路徑上不能有兩個連續的紅色節點)
- 從任一節點到其每個葉子的所有路徑都包含相同數目的黑色節點(每一條樹鏈上的黑色節點數量(稱之為“黑高”)必須相等)。
3.5紅黑樹總結
紅黑樹可以說是十分復雜的,我在學習的時候並沒有去認真細看當中的處理細節,只是大概的過了一遍,知道了整體~
有了前輩很多優質的資料,相信要等到想要理解其中的細節,花點力氣和時間還是可以掌握一二的。
紅黑樹參考資料:
- https://blog.csdn.net/chen_zhang_yu/article/details/52415077
- https://riteme.github.io/blog/2016-3-12/2-3-tree-and-red-black-tree.html#fn:red-is-left
- http://www.sohu.com/a/201923614_466939
- https://www.jianshu.com/p/37c845a5add6
- https://www.cnblogs.com/nullzx/p/6111175.html
- https://blog.csdn.net/fei33423/article/details/79132930
四、總結
這篇主要介紹了Map集合的基礎知識,了解Map的常用子類~
簡單介紹了散列表和紅黑樹,他倆作為Hashxxx和Treexxx的底層,了解其整體思想和相關基礎在后續看源碼也不至於那么懵~
后續會去看Map常用子類的源碼,文章敬請期待~~~~
如果文章有錯的地方歡迎指正,大家互相交流。習慣在微信看技術文章,想要獲取更多的Java資源的同學,可以關注微信公眾號:Java3y。為了大家方便,剛新建了一下qq群:742919422,大家也可以去交流交流。謝謝支持了!希望能多介紹給其他有需要的朋友
參考資料:
- 《Core Java》