關於集合(每日筆記)
什麼是集合?
集合是作爲數據結構的載體,可以對元素景象加工和輸出,以一定的算法實現最基本的增刪改查功能因此集合是所有編程語言的基礎
數組(瞭解)
數組可以使用索引下標進行快速定位,並獲取指定位置的元素
數組的下標是從0開始的,這源於BCPL語言,它將指針設置在0位置,用數組下標作爲直接偏移量進行計算
如果下標從1開始,計算機偏移量就要使用當前下標減1的操作.加減法運算在數組下標使用頻率極高的場景下,這種運算十分耗時。
集合的體系結構
第一類:
按照單個元素存儲的Collection,在繼承樹中List接口與Set接口都實現了Collection接口
- Collection
- List
- ArrayList
- LinkedList
- Vector(瞭解)
- Set
- HashSet
- TreeSet
- LinkedHashSet
- Queue(瞭解)
- LinkedBlockingQueue(瞭解)
- List
第二類
按照Key-Value存儲的Map(鍵值對)
- Map
- HashMap
- TreeMap
- ConcurrentHashMap
- HashTable
- LinkedHashMap
List
List集合是線性數據結構的主要實現,集合元素通常存在明確的上一個和下一個元素,也存在明確的第一個元素和最後一個元素。
特點:
有序、可重複、可通過索引值操作元素
ArrayList
- 底層是數組,查詢快,增刪慢。
- ArrayList是容量可以改變的非線程安全集合。內部實現使用數組進行存儲,集合擴容時會創建更大的數組空間,把原有數據複製到新數組中。
- ArrayList支持對元素的快速隨機訪問,但是插入與刪除時速度通常很慢,因爲這個過程很可能需要移動其它元素。
LinkedList
- 底層是鏈表,查詢慢,增刪快。
- LinkedList本質是雙向鏈表。與ArrayList相比,LinkedList的插入和刪除速度更快,但是隨機訪問速度則很慢。
Set
特點:
特點:無序、元素唯一
HashSet
- HashSet從源碼分析是使用HashMap來實現的,只是value固定爲一個靜態對象,使用key保證集合元素的唯一性,但它不保證集合元素的順序。
TreeSet
-
TreeSet從源碼分析是使用TreeMap來實現的,底層爲樹結構,在添加新元素到集合中時,按照某種比較規則將其插入合適的位置,保證插入後的集合仍然是有序的。
-
Integer和String對象都可以進行默認的TreeSet排序,而自定義類的對象是不可以的,自己定義的類必須實現Comparable接口或者定義外部比較器Comparator,纔可以正常使用。
-
LinkedHashSet
- LinkedHashSet繼承自HashSet,內部使用鏈表維護了元素插入順序。
Map
Map集合是以Key-Value鍵值對作爲存儲元素實現的哈希結構,Key按哈希函數計算後是唯一的,Value則是可以重複的。Key、Value是否允許爲null,以實現類約束爲準。
Hashtable因爲性能瓶頸已經被淘汰;廣泛使用的HashMap線程是不安全的;ConcurrentHashMap是線程安全的,在JDK8中進行了鎖的大幅度優化;TreeMap是Key有序的Map類集合。
在多線程併發場景中,優先推薦使用ConcurrentHashMap,而不是HashMap。
Hashtable
- Key不允許爲null,Value不允許爲null。
- 早期Java類庫提供的哈希表的實現。
- 線程安全:涉及到修改Hashtable的方法,使用Synchronized修飾。
- 串行化的方式運行,性能較差。
TreeMap
- Key不允許爲null,Value允許爲null。
- TreeMap是按照Key的排序結果來組織內部結構的Map類集合,它改變了Map類散亂無序的形象。
- TreeMap的插入操作就是按Key的對比往下遍歷,大於比較節點值的向右走,小於比較節點值的向左走,先按照二叉查找樹的特性進行操作,後續會重新着色和旋轉,保持紅黑樹的特性。
- TreeMap是線程不安全的集合,不能在多線程之間進行共享數據的寫操作。在多線程進行寫操作時,需要添加互斥機制,或者把對象放在Collections.synchronizedMap(treeMap)中實現同步。
HashMap
- Key允許爲null,Value允許爲null。
- Java8以前底層數據結構:數組+鏈表。查詢性能惡化:從O(1)變成O(N)。
- Java8及以後底層數據結構:數組+鏈表+紅黑樹。最壞情況下性能從O(N)提高到O(logN)。默認情況下鏈表長度超過8變成紅黑樹,紅黑樹節點樹小於6變回鏈表。
- Java8HashMap的put()方法的邏輯:
- 如果HashMap未被初始化過,則初始化。
- 對Key求Hash值,然後再計算下標
- 如果沒有碰撞,直接放入桶中
- 如果碰撞了,以鏈表的方式鏈接到後面。
- 如果鏈表長度超過閥值,就把鏈表轉成紅黑樹。
- 如果鏈表長度低於6,就把紅黑樹轉回鏈表。
- 如果節點以及存在就替換舊值。
- 如果桶滿了(容量16*加載因子0.75),就需要resize(擴容2倍後重排)。
ConcurrentHashMap
- Key不允許爲null,Value不允許爲null。
- 早期的ConcurrentHashMap通過分段鎖Segment來實現。
- Segment 通過繼承 ReentrantLock 來進行加鎖,所以每次需要加鎖的操作鎖住的是一個 segment,這樣只要保證每個 Segment 是線程安全的,也就實現了全局的線程安全。
- (Java8)ConcurrentHashMap:CAS+synchronized使鎖更細化。
- 首先使用無鎖操作CAS插入頭節點,失敗則循環重試。若頭節點已存在,則嘗試獲取頭節點的同步鎖,再進行操作。
在Java8中,HashMap、Hashtable、ConcurrentHashMap的區別
* HashMap線程不安全,數組+鏈表+紅黑樹
* Hashtable線程安全,鎖住整個對象,數組+鏈表
* ConcurrentHashMap線程安全,CAS+同步鎖,數組+鏈表+紅黑樹
* HashMap的key、value均可以爲null,而其它的兩個類不支持。