集合
常用集合關系圖
集合分為單列集合和雙列集合兩種:
一.單列集合:
Collection是單列集合的頂級接口:
其中有三類集合:
1.List(ArrayList,LinkedList,Vector等)
有序的可以重復的集合,JDK1.6和JDK1.7的時候創建集合時初始容量是10,而JDK1.8中默認容量是0,首次添加元素不超過10時,容量變為10。
List的加載因子系數<=1,即當元素個數超過(容器長度*加載因子系數)時,進行擴容
①ArrayList集合是動態數組的數據結構實現的,它的隨機訪問和遍歷的效率比較高,在需要頻繁的讀取集合中的元素的時候,推薦使用ArrayList,但是增刪操作要影響數組內的其他數據的下標,所以增刪操作的效率比較慢。
ArrayList每次擴增容量為原本的1.5倍,JDK1.8之后算法為oldCapacity+(oldCapacity >>1)。
ArrayList的擴容上限約21億,int的最大值。
②LinkedList集合是雙向的鏈表的數據結構實現,沒有下標,通過鏈來連接數據,在非首尾的增加和刪除操作時,LinkedList比ArrayList的效率要高,推薦使用LinkedList,但是查詢的時候,每次都要從首位移動指針往后依次查找,所以查詢的效率比較慢。
③Vector集合與ArrayList集合類似,內部都是維護一個數組,只不過前者在方法上加上了synchronized關鍵字、線程安全、效率低,后者線程不安全,但是效率高。
Vector每次擴增容量為原來的兩倍。
2.Set(HashSet等)
除了TreeSet集合外元素無序,不允許元素重復。
Set的擴容量為原來的2倍,加載因子為0.75。
①HashSet集合是基于HashMap實現的,HashSet底層使用HashMap來保存元素,HashMap是數組和鏈表的數據結構(下面HashMap具體介紹),HashSet和HashMap的初始容量都是16
3.Queue/Deque
①Queue隊列,隊列是一種特殊的線性表,它只允許在表的前端進行刪除操作,而在表的后端進行插入操作,LinkedList類實現了Queue接口,可以吧LinkedList當成Queue來用
②Deque雙端隊列,可以在兩端進行插入和刪除操作
二.雙列集合:
1.Map(HashMap,Hashtable等)
①HashMap集合線程非安全,使用鍵值對(key-value)的方式存儲數據,允許key和value為null,鍵(key)不能重復,值(value)可以重復,它和HashSet是基于哈希表的鏈表散列的數據結構,即底層是數組和鏈表(模擬指針)實現的,到了JDK1.8后變成了數組+鏈表+紅黑樹。
HashMap底層是通過一個transient(防止反序列化) Node[]table node數組實現的,接下來單個Node數據類型:是HanshMap靜態內部類。靜態內部類中有一個成員變量:
Node<K,V>next;
?通過該成員變量,其實底層用的是單向鏈表,性能低
HashMap 基于 Hash 算法實現的,我們通過 put(key,value)存儲,get(key)來獲取。當傳入 key 時,HashMap 會根據 key. hashCode() 計算出 hash 值,根據 hash 值將 value 保存在 bucket 里。當計算出的 hash 值相同時并且equals比較的值不同時,我們稱之為 hash 沖突,HashMap 的做法是用鏈表和紅黑樹存儲相同 hash 值的 value。當 hash 沖突的個數比較少時,使用鏈表否則使用紅黑樹。
在向HashMap中添加數據的時候,會先調用hashCode方法計算并比較hash值,當hash相同的情況在調用equals方法比較,當兩個均不相同時判定集合中不存在,然后將數據插入到集合中。兩個對象的hash值相同,但是不一定是同一個對象,也就是說自身的值可能會不相同。
(例如:String a ="通話";String b="重地";這兩個字符串生成的hash值同為1179395,但是其自身的值卻不相同)
從圖中可以看出:?
1)HashMap繼承于AbstractMap類,實現了Map接口。Map是"key-value鍵值對"接口,AbstractMap實現了"鍵值對"的通用函數接口。?
2)HashMap是通過"拉鏈法"實現的哈希表。它包括幾個重要的成員變量:table,?size,?threshold,?loadFactor,?modCount。
table是一個Entry[]數組類型,而Entry實際上就是一個單向鏈表。哈希表的"key-value鍵值對"都是存儲在Entry數組中的。
size是HashMap的大小,它是HashMap保存的鍵值對的數量。?
threshold是HashMap的閾值,用于判斷是否需要調整HashMap的容量。threshold的值="容量*加載因子",當HashMap中存儲數據的數量達到threshold時,就需要將HashMap的容量加倍。
loadFactor就是加載因子。?
modCount是用來實現fail-fast機制的。
LinkedHashMap:底層也是一個Entry<k,v>數組,接下來單個Entry數據類型
Entry<K,V>before,after;
②Hashtable線程安全,是線程安全的,但是其同步鎖是全局鎖,效率很低,所以Hashtable現在是保留類不建議使用。
單線程的情況下HashMap效率高,推薦使用;多線程的情況下可以使用java.util.concurrent包下的ConcurrentHashMap替代,它之前采用Segment方式的分段同步鎖,將所有的數據分割成幾部分的數據區域,一個區域一把鎖,訪問不同區域時不會收到影響,JDK1.8版本之后采用優化后的synchronized,將每一個數據分別鎖住,不同數據間的訪問不會收到影響,效率大大增加,并且線程是安全的,多線程并發情況下很推薦使用!
雙列集合的五種遍歷方式
1)通過map.keySey();獲取所有的key的Set集合,然后可以通過增強for自動迭代,也可以獲取迭代器iterator然后用while循環進行手動遍歷
2)通過map.values();獲得所有的value的集合,返回值為Collecton,然后用增強for自動迭代
3)(推薦)通過map.entrySet();獲取所有的鍵值對的Set集合,Set集合中保存的是每一個鍵值對(Map.Entry),然后可以通過增強for自動迭代,也可以獲取迭代器iterator然后用while循環進行手動遍歷
三.關于迭代器
迭代器是Iterator接口:該接口中有三個核心方法 ,維護指針可以向下移動(next),移動到指定位置后,取出當前位置的元素(next),以及重置指針操作(remove)。
為什么數組和集合可以使用for循環進行迭代遍歷?
解析:所有的數組和集合都實現了Iterable接口,Iterable接口中只有一個方法,iterator方法返回值類型是Iterator類型,我們將思路轉到Iterator,我們發現該接口三個方法。hasNext,next和remove,最主要的是hasNext和next。他們在底層幫我們去維護的可以被迭代數組或者集合的迭代策略。
四.JDK版本差異更新
排序算法
線程安全的集合
集合擴容的算法
五.集合在特定場景下的應用方案
最近瀏覽可以選取LinkedList
先進先出可以考慮Queue隊列
先進后出可以考慮Stack,遞歸,壓棧 ,彈棧?