想要了解集合,首先要知道一個東西,叫數據結構。所謂數據結構,其實就是計算機存儲,組織數據的方式。
常用的數據結構有8大類
數組,鏈表,樹,堆,棧,隊列,哈希表,圖
在集合中比較常見的是數組,鏈表,紅黑樹
什么是集合
Java 集合(Collection)是 Java 提供的一套用于存儲和操作多個對象的框架,位于
java.util
包下。它替代了傳統的數組,提供了更靈活、功能更豐富的數據結構,以及便捷的操作方法。
Java 集合兩大體系Collection和Map
Collection是單列集合(單列集合元素存儲方式就是一個個元素存儲),
Collection 接口的主要子接口
List:有序、可重復的集合
List最大的特點就是存儲的元素是有順序的,并且可以存儲重復的元素
ArrayList
:基于動態數組實現,查詢快,增刪慢LinkedList
:基于雙向鏈表實現,增刪快,查詢慢Vector
:線程安全的動態數組(古老類,效率較低)Set:無序、不可重復的集合
Set最大的特點就是存儲的元素是無順序的,并且不可以存儲重復的元素
HashSet
:基于哈希表實現,無序,查詢快LinkedHashSet
:繼承 HashSet,維護元素插入順序TreeSet
:基于紅黑樹實現,元素有序(自然排序或定制排序)Queue:隊列,通常按 FIFO(先進先出)原則操作
LinkedList
:可作為隊列使用PriorityQueue
:優先級隊列,元素按優先級排序
Map是雙列集合(雙列集合元素存儲方式是以key,value鍵值對的方式進行存儲)
Map 接口的主要實現類
HashMap
:基于哈希表實現,無序,查詢快LinkedHashMap
:繼承 HashMap,維護鍵值對的插入順序TreeMap
:基于紅黑樹實現,鍵有序Hashtable
:線程安全的哈希表(古老類,效率較低)ConcurrentHashMap
:線程安全的 HashMap,并發性能好
ArrayList和LinkedList和Vector
ArrayList底層的數據結構是數組,而LinkedList底層的數據結構是鏈表
Vector支持線程同步,是線程訪問安全的,ArrayList線程不安全 。
數組的特點就是通過索引來查詢數據,因此查詢數據速度比較快,而因為數組的內存空間是連續性的因此在添加數據和刪除數據的時候,因此效率沒這么快
鏈表的特點就是內存空間不是連續性的,因此查詢的時候需要遍歷檢索,效率沒這么高,而元素的底層是由prev,元素和next三部分組成的節點。因此在增刪數據的時候只需要把節點的next,斷開原本的prev,然后連接新的prev,因此無需移動元素,效率更高,被斷開的節點會慢慢地被gc回收
ArrayList動態擴容
ArrayList默認數組的大小為10,擴容的時候采用的是采用移位運算
ArrayList的擴容因子為1.5
ArrayList和LinkedList的使用場景
在工作中對元素進行增刪操作時使用LinkedList,而進行查詢的操作使用的是ArrayList
ArrayList底層結構是基于數組,因為數組最大的特點就是有索引作為下標,查詢時比較方便快捷,但在增刪操作時要進行元素位置的移動,因此效率比較慢。
LinkedList的底層結構基于鏈表,查詢的時候回通過這般搜索的方式進行查找因此效率會比較慢,
但在增刪操作時只需要將鏈表節點的頭尾指針進行修改即可,因此效率會比較快。
在ArrayList中訪問元素的最糟糕的時間復雜度是O(1),而在LinkedList中可能就是O(n)了。在ArrayList中增加或者刪除某個元素時候,如果觸發到了擴容機制,那么底層就會調用到System.arraycopy方法,被native修飾,該方法會直接通過內存復制,省去了大量的數組尋址訪問等時間。但是相比于LinkedList而言,在頻繁的修改元素的情況下,選用LinkedList的性能會更加好一點
如果去學習過jvm的話,應該會對“內存碎片“這個名詞比較熟悉。
基于數組結構的數據在存儲信息的時候都需要有連續的內存空間,
所以如果當內存碎片化情況較為嚴重的時候,可能在使用ArrayList的時候會有OOM的異常拋出。
復制某個ArrayList到另一個ArrayList中去
- 使用clone()方法,比如ArrayList newArray = oldArray.clone();
- 使用ArrayList構造方法,比如:ArrayList myObject = new ArrayList(myTempObject);
- 使用Collection的copy方法。
fail-fast機制
在ArrayList設計的時候,其實還包含有了一個modCount參數,
這個參數需要和expectedModCount 參數一起使用,expectedModCount參數在進行修改的時候會被modCount進行賦值操作,
當多個線程同時對該集合中的某個元素進行修改之前都會進行expectedModCount 和modCount的比較操作,
只有當二者相同的時候才會進行修改,兩者不同的時候則會拋出異常。
COW容器
jdk1.5之前,由于常用的ArrayList并不具有線程安全的特性,因此在1.5之后的并發包里面出現了CopyOnWrite容器,簡稱為COW。
通俗的理解是當我們往一個容器添加元素的時候,不直接往當前容器添加,而是先將當前容器進行Copy,復制出一個新的容器,然后新的容器里添加元素,添加完元素之后,再將原容器的引用指向新的容器。
這樣做的好處是我們可以對CopyOnWrite容器進行并發的讀,而不需要加鎖,因為當前容器不會添加任何元素。
所以CopyOnWrite容器也是一種讀寫分離的思想,讀和寫不同的容器。
并發包juc里面的CopyOnWriteArrayList中,核心原理主要是通過加入了ReentrantLock來保證線程安全性,從而解決了ArrayList的線程安全隱患問題。
集合的選擇建議
- 需要有序可重復:選擇
ArrayList
(查詢多)或LinkedList
(增刪多)- 需要無序不可重復:選擇
HashSet
- 需要排序的集合:選擇
TreeSet
(元素)或TreeMap
(鍵)- 需要鍵值對存儲:選擇
HashMap
(一般情況)或LinkedHashMap
(需順序)- 多線程環境:考慮使用
ConcurrentHashMap
等線程安全集合
集合的常用操作
- 添加元素:
add()
(Collection)、put()
(Map)- 刪除元素:
remove()
- 查找元素:
contains()
- 獲取大小:
size()
- 清空集合:
clear()
- 遍歷元素:增強 for 循環、迭代器(Iterator)