集合整體框架圖
各集合框架的概述
1. Collection(常用List和Set,不常用Queue和Vector),單元素集合。
2. Map(常用HashMap和TreeMap,不常用HashTable),key-value映射關系。
3. Iterator(迭代器)
4. Comparable和Comparator比較器
5. Collections和Arrays工具類
Java中結合和數組的比較
1. 數組的長度在初始化時指定,只能保存定長的數據。
而集合可以保存不確定長度的數據。同時可以保存具有映射關系的數據(即關聯數組,鍵值對key-value)。
2. 數據元素既可以是基本類型的值,也可以是對象,集合只能保存對象(實際上是只保存對象的引用變量),基本數據類型的變量要轉換成對應的包裝類才能放入集合中。
常用集合特性概述
List系
List的特點:元素有放入順序,元素可以重復。
List接口的三個實現類:ArrayList、LinkedList和Vector。
ArrayList、LinkedList和Vector的區別
LinkedList:底層是基於鏈表實現,鏈表內存是散亂的,每一個元素存儲本身內存地址的同時也存儲下一個元素的內存地址。鏈表增刪快,查找慢。
ArrayList和Vector:兩者底層都是基於數組實現的,查詢快,增刪慢。兩者之間的區別是ArrayList是線程非安全的,效率高;而Vector是線程安全的,效率低。
ArrayList的初始化大小是10,擴容策略是1.5倍原元素數量的大小。
選擇標准
如果涉及到“動態數組”、“棧”、“隊列”、“鏈表”等結構,應該考慮用List,具體選擇哪個List實現類,根據下面的標准來取舍:
1. 對於需要快速插入、刪除元素,應該使用LinkedList。
2. 對於需要快速隨機訪問元素,應該使用ArrayList。
3. 對於“單線程環境”或者“多線程環境,但List僅僅只會被單個線程操作”,此時應該使用非同步的類(如ArrayList)。對於“多線程環境,且List可能同時被多個線程操作”,此時,應該使用同步的類(如Vector)。
Set系
Set的特點:元素放入無順序,元素不可重復。
Set接口的三個實現類:LinkedSet、HashSet和LinkedHashSet。
HashSet(底層是通過HashMap實現)底層通過HashCode和equals去重。
HashSet中判斷集合元素相等
兩個對象比較 具體分為如下四個情況:
1.如果有兩個元素通過equal()方法比較返回false,且它們的hashCode()方法返回不相等,HashSet將會把它們存儲在不同的位置。
2.如果有兩個元素通過equal()方法比較返回true,但它們的hashCode()方法返回不相等,HashSet將會把它們存儲在不同的位置。
3.如果兩個對象通過equals()方法比較不相等,但hashCode()方法比較相等,HashSet將會把它們存儲在相同的位置,在這個位置以鏈表式結構來保存多個對象。這是因為當向HashSet集合中存入一個元素時,HashSet會調用對象的hashCode()方法來得到對象的hashCode值,然后根據該hashCode值來決定該對象存儲在HashSet中存儲位置。
4.如果有兩個元素通過equal()方法比較返回true,且它們的hashCode()方法返回true,HashSet將不予添加。
HashSet判斷兩個元素相等的標准:兩個對象通過equals()方法比較相等,並且兩個對象的hashCode()方法返回值也相等。
注意:HashSet是根據元素的hashCode值來快速定位的,如果HashSet中兩個以上的元素具有相同的hashCode值,將會導致性能下降。所以如果重寫類的equals()方法和hashCode()方法時,應盡量保證兩個對象通過hashCode()方法返回值相等時,通過equals()方法比較返回true。
LinkedHashSet類
LinkedHashSet是HashSet對的子類,也是根據元素的hashCode值來決定元素的存儲位置,同時使用鏈表維護元素的次序,使得元素是以插入的順序來保存的。當遍歷LinkedHashSet集合里的元素時,LinkedHashSet將會按元素的添加順序來訪問集合里的元素。但是由於要維護元素的插入順序,在性能上略低與HashSet,但在迭代訪問Set里的全部元素時有很好的性能。
注意:LinkedHashSet依然不允許元素重復,判斷重復標准與HashSet一致。
補充:HashSet的實質是一個HashMap。HashSet的所有集合元素,構成了HashMap的key,其value為一個靜態Object對象。因此HashSet的所有性質,HashMap的key所構成的集合都具備。可以參考后續文章中HashMap的相關內容進行比對。
TreeSet類
TreeSet是SortedSet接口的實現類,正如SortedSet名字所暗示的,TreeSet可以確保集合元素處於排序狀態。此外,TreeSet還提供了幾個額外的方法。
1 comparator():返回對此 set 中的元素進行排序的比較器;如果此 set 使用其元素的自然順序,則返回null。 2 first():返回此 set 中當前第一個(最低)元素。 3 last(): 返回此 set 中當前最后一個(最高)元素。 4 lower(E e):返回此 set 中嚴格小於給定元素的最大元素;如果不存在這樣的元素,則返回 null。 5 higher(E e):返回此 set 中嚴格大於給定元素的最小元素;如果不存在這樣的元素,則返回 null。 6 subSet(E fromElement, E toElement):返回此 set 的部分視圖,其元素從 fromElement(包括)到 toElement(不包括)。 7 headSet(E toElement):返回此 set 的部分視圖,其元素小於toElement。 8 tailSet(E fromElement):返回此 set 的部分視圖,其元素大於等於 fromElement。
Map系
Map的特點:存儲的元素是鍵值對,在JDK1.8版本中是Node,在老版本中是Entry。
Map接口有四個實現類:HashMap、HashTable、LinkedHashMap和ConcurrentHashMap。
HashMap類
HashMap是非線程安全,高效,支持null的key和value,底層實現是數組和鏈表,通過HashCode方法和equals方法保證鍵的唯一性。
如果需要使用線程安全的Map可以有兩種方式:
1. 采用HashTable
2. 采用Collections.synchronizedMap(hashMap)方式進行同步。
解決沖突主要有三種方法:定址法、拉鏈發和再散列發。
HashMap是采用拉鏈法解決哈希沖突的,拉鏈法是將相同hash值的對象組成一個鏈表放在hash值對應的槽位。
HashMap的初始化大小是16,擴展因子是0.75,擴容策略是2倍原容量的大小。
put的大致流程
1. 通過hashCode方法計算出key的hash值
2. 通過hash%length計算出存儲在table中的index(源碼中是使用hash&(length-1),這樣結果相同,但是更快)。
3. 如果此時table[index]的值為空,那么就直接存儲,如果不為空那么就鏈接到這個數所在的鏈表的頭部。(在JDK1.8中,如果鏈表長度大於8就轉化成紅黑樹)
get的大致流程
1. 通過通過hashCode方法計算出key的hash值
2. 通過hash%length計算出存儲在table中的index(源碼中是使用hash&(length-1),這樣結果相同,但是更快)。
3. 遍歷table[index]所在的鏈表,只有當key與該節點中的key的值相同時才取出。
ConcurrentHashMap類
是從JDK1.5之后提供的一個HashTable的替代實現,采用分段鎖機制,一個map中的元素分成很多的segment,通過lock機制可以對每個segment加讀寫鎖,從而提高map的效率,底層實現采用數組+鏈表+紅黑樹的存儲結構。
HashTable類
線程安全,低效,不支持null的key和value。
SortedMap類
有一個實現類:TreeMap會存儲放入元素的順序。