好,首先我們根據這張集合體系圖來慢慢分析。大到頂層接口,小到具體實現類。
首先,我想說為什么要用集合?簡單的說:數組長度固定,且是同種數據類型。不能滿足需求。所以我們引入集合(容器)來存儲任意數據類型的可變大小的數據。
來了解下數組:
數組有靜態、動態之分。但是其長度都是固定的,並且其內部只能存儲同一種數據類型的數據。除非是Object類型的數組,它可以存儲任意類型的數據。
數組的存儲方式?數據存儲結構分為順序存儲、鏈接存儲、索引存儲、散列存儲。
數組是基於順序存儲方式。數組就是在內存中開辟一塊連續的、大小相同的空間,用來存儲數據。
數組查詢速度快:因為基於索引查詢。但是增刪慢。為什么增刪慢呢?因為數組的元素是連續的(索引是連續的)。如果要增刪元素時,會發生索引位置的移動,且是重新創建了一個新數組。所以增刪慢。
java集合體系結構中主要按兩種接口分類:
1.Collection
2. Map
//查看jdk1.8源碼
首先分析Collection,它是一個接口,同時它繼承了頂級Iterable接口,為什么要繼承這個接口呢?主要是使用接口中特有
方法,可以通過iterator迭代器遍歷整個集合。在jdk1.8中,Iterable接口中有forEach方法、spliterator()方法。
同時,List接口繼承了Collection接口.List是存取有序可重復且有索引的,同時允許元素為null。這里所說的有序是元素的存取順序。而Set是無序不可重復無索引。但是Set的無序我們一般是指HashSet,因為它不能保證元素的存取順序和自然排序。而LinkedHashSet:保證元素的添加順序。TreeSet:保證元素的自然排序。可能知道的人很容易理解,不知道的人不理解就很難記憶。通過代碼,也可以清楚看到是否有序,是否重復的特性。由於篇幅,演示代碼省略。接下來我們逐一分析接口下的實現類都有什么特性?
List接口下的實現類
ArrayList:底層是使用了Object數組實現的,查詢速度快,增刪慢。非同步的(線程不安全類),因此效率高。數組默認容量是10,當長度不夠時自動增長0.5倍,也就是原數組的1.5倍。
使用場景:頻繁查詢時,增刪較少時。
LinkedList:底層結構是雙向鏈表,不需要在內存中開辟一段連續的內存空間。而是每個元素有一個下一元素地址]這樣的內存結構。增加時只需要改變兩個節點之間的引用關系即可。來組成一個新的節點。刪除時只要刪除兩個節點的引用即可。而查詢時,必須從頭部開始查詢。所以效率慢。查詢慢,增刪快。同時它是非同步的(線程不安全類)。https://blog.csdn.net/qq_35120695/article/details/56573865
Vector: 底層也是基於動態數組實現。線程安全類,但是效率低,基本不使用。
Set接口下的實現類
HashSet:底層數據結構是哈希表,而jdk1.8中哈希表底層采用數組+鏈表+紅黑樹。其實現set接口,底層使用HashMap來保存所有元素。特點:無序不可重復。線程不安全。其是如何保證去重原理的呢?
從源碼中我們可以看出:
它的add()方法實際上調用的是HashMap中的put()方法,把要添加進HashSet中的元素當做key存入,而value則是一個固定值:一個Object類對象。
先用hashCode()方法獲得傳入元素的哈希值,在集合中查找是否包含哈希值相同的元素,如果相同,則繼續進行比較它們地址值,一般地址值都是不相同的,所以最后會用equals()方法比較對象內的屬性值。
比較結果全為false就存入,如果比較結果有true則不存。
TreeSet:底層使用TreeMap來進行存儲,而TreeMap底層基於紅黑樹數據結構,規則是左小右大。有序不可重復。有序指對元素進行自然排序。同時支持定制排序。線程不安全類。
紅黑樹規則補充:
紅黑樹:也可以叫二叉樹
規則:左節點小於等於父節點,右節點大於父節點。
為了保證樹的左右平衡,紅黑樹采用節點標色的方式,將節點標記為紅色或黑色。根節點(沒有父節點)為黑色。
每個葉子節點都是null值且是黑色。
如果一個節點為紅色,則它的子節點必須是黑色
新插入節點的顏色為紅色
左旋,右旋、顏色反轉操作保持樹平衡
jdk1.8中加入紅黑樹目的就是解決鏈表的查詢效率低的問題。
HashMap默認容量是16,源碼顯示1<<4,就是1向左移動4為,即為16.
當鏈表長度>8時,則將鏈表轉為紅黑樹。
當紅黑樹內數量<6時,將紅黑樹轉為鏈表
TreeSet注意事項:
1、往TreeSet添加元素的時候,如果元素本身具備了自然順序的特性,那么就按照元素本身的自然順序特性進行存儲;
2、如果元素本身不具備自然 順序的特性,那么該元素所屬的類必須要實現Comparable接口,並重寫compareTo()
方法,把元素的比較規則定義在compareTo()方法上;
3、如果比較元素的時候,compareTo()方法返回的是0,那么該元素就被視為重復元素,不允許添加;
LinkedHashSet:底層是哈希表和鏈表。其繼承了HashSet,實現set接口。
由鏈表保證元素的有序(存取順序)。由哈希表保證元素的唯一(不重復)。內部是通過 LinkedHashMap 來實現的。
Map接口下的實現類
HashMap:首先看源碼分析,它繼承了AbstractMap類,基於哈希表的Map接口實現。是以key-value存儲形式。主要用來存放鍵值對。它是非同步(線程不安全)的。它的key-value都可以為null,且映射是無序的。
jdk1.8之前,HashMap由數組+鏈表組成。數組是主體,鏈表則是主要為了解決哈希沖突(兩個對象通過hashcode方法計算的哈希碼值一致導致數組索引值相同)而存在的。
在jdk1.8之后,當鏈表長度大於閾值(紅黑樹的邊界值默認為8)並且當前數組長度大於64時,此時所有數據改為采用紅黑樹存儲。
補充:將鏈表轉為紅黑樹前會進行判斷,即使閾值大於8,但是數組長度小於64,此時並不會將鏈表轉為紅黑樹,而是進行數組擴容。這樣做的目的是因為數組長度比較小,盡量避開紅黑樹結構。因為這種情況下使用紅黑樹結構反而效率低,因為紅黑樹需要通過左旋、右旋、變色等操作來保持平衡。同時數組長度小於64時,查詢時間更快些。
特點:
- 存取無序
- 鍵值位置都可以為null,但是鍵位置只能是一個null且鍵唯一。
- jdk1.8之前數據結構是數組+鏈表。jdk1.8之后是:數組+鏈表+紅黑樹。
- 當鏈表長度大於8,並且數組長度大於64時,將鏈表轉為紅黑樹,目的是提高查詢效率
Hashmap的存儲過程如下圖:
如果創建集合時,我們用有參構造,指定初始容量不是2的n次冪情況分析
查看底層算法如下:
如果指定初始容量不是2的n次冪,底層會進行一頓右移和或運算。
TreeMap:
通過紅黑樹實現,默認情況下通過Key值的自然順序進行排序,也可以通過比較器排序。非線程安全類
https://www.cnblogs.com/LiaHon/p/11221634.html
HashTable:
HashMap可以允許存在一個為null的key和任意個為null的value,但是HashTable中的key和value都不允許為null.而當HashTable遇到null時,他會直接拋出NullPointerException異常信息。
Hashtable的方法是同步(線程安全,效率低)的,而HashMap的方法不是。所以有人一般都建議如果是涉及到多線程同步時采用HashTable,沒有涉及就采用HashMap。
HashTable中hash數組默認大小是11,增加的方式是 old*2+1。HashMap中hash數組的默認大小是16,而且一定是2的指數。
https://blog.csdn.net/varyall/article/details/80992123
CurrentHashMap
https://www.cnblogs.com/supertang/p/4149786.html
擴容圖示: