Java集合知識匯總
一、List(列表)——線程不安全的數據結構
1.1、List數組(ArrayList)
底層:一個可動態擴容的數組,與普通數組的區別就是它是沒有固定大小的限制
特點:順序存儲,讀速度、更新快,增刪慢;內存相鄰,根據Index讀取的時間復雜度是O(1);可以存儲重復元素,但線程不安全
擴容機制:如果當前元素放不下,則擴容至1.5倍,且擴容空間大於等於1
1.2、List鏈表(LinkedList)
底層:節點,存下數據的引用對象(非數據),節點之間使用引用相關聯
特點:順序存儲,實現了Queue、Deque接口,可作為隊列使用;查找慢,增刪快,可以存儲重復元素,但線程不安全
擴容機制:無固定大小,不需要擴容
Api:
public E getLast() //獲取最后一個元素
public E removeFirst() // 移除第一個元素,並返回
public E removeLast() // 移除最后一個元素,並返回
public void addFirst(E e) //加入頭部
public void addLast(E e) //加入尾部
public void add(E e) //加入尾部
public boolean contains(Object o) //是否包含 元素 o
public E peek() //獲取頭部第一個元素
public E element() // 獲取頭部第一個元素,不存在則報錯
public E poll() //獲取頭部第一個元素,並移除
public boolean offer(E e) // 調用 add(E e)
public boolean offerFirst(E e) // 調用 addFirst
public boolean offerLast(E e) // 調用 addLast
public void push(E e) //在頭部壓入一個元素
public E pop() //彈出第一個元素,並移除。不存在則報錯
1.3、List總結
ArrayList和LinkedList:
共同點:順序存儲,可以存儲重復元素,線程不安全
不同點:ArrayList底層數組,內存相鄰,因此擅長讀改,LinkedList底層節點之間的使用引用相關聯,因此擅長增刪
Iterator 和 fast-fail、fail-safe機制:
-
Java Iterator(迭代器)不是一個集合,它是一種用於訪問集合的方法,可用於迭代 List 和 Set 等集合,主要有hashNext(),next(),remove()三種方法
-
fail-fast 是Java集合(Collection)的一種錯誤機制。當多個線程對同一個集合進行修改結構操作,使用集合的迭代器iterator,會首先檢測是否有對集合的並發修改,進而產生ConcurrentModificationException 異常提示
-
fail-safe:保證在對任何集合結構的修改操作都基於先復制再修改 進行的,即先copy一個新的集合對象,然后對新的集合對象進行修改,最后將新的集合對象替換掉老的集合對象(老的集合對象的地址指向新的集合對象)。java.util.concurrent包下采用的是fail-safe機制
-
- 缺點1:對集合的復制copy會產生大量的對象,造成內存空間的浪費
- 缺點2:無法保證集合迭代過程中獲取的集合數據是最新的內容
如何保證線程安全呢?
- plan_A:使用 Vector
- plan_B:使用 Collections.synchronized() 返回線程安全的 List
- plan_C:使用 CopyOnWriteArrayList
Collections.synchronized()舉例::
List<String> list = Collections.synchronizedList(new ArrayList<String>());
list.add("1");
synchronized (list) {
Iterator i = list.iterator(); // Must be in synchronized block
while (i.hasNext()) {
System.out.println(i.next());
}
}
Collections.synchronizedList()添加數據不用鎖(底層代碼已經實現),遍歷數據用鎖
CopyOnWriteArrayList 的線程安全:
CopyOnWriteArrayList 在寫的時候會加鎖,為了保證寫安全,會在寫操作時復制一個新數組來操作,然后覆蓋舊的數組,不會影響讀的性能
CopyOnWriteArrayList 的缺點:
- CopyOnWrite 在進行寫操作的時候,內存里會同時駐扎兩個對象的內存,導致內存的浪費
- CopyOnWrite 容器只能保證數據的最終一致性,不能保證數據的實時一致性。如果你希望寫入的的數據,馬上能讀到,請不要使用CopyOnWrite容器,沒有阻塞等待的概念
CopyOnWriteArrayList 和 Collections.synchronizedList 區別:
-
CopyOnWriteArrayList 的寫操作性能較差,而多線程的讀操作性能較好
-
Collections.synchronizedList的寫操作性能比CopyOnWriteArrayList在多線程操作的情況下要好很多,而讀操作因為是采用了 synchronized關鍵字的方式,其讀操作性能並不如CopyOnWriteArrayList
List的Api:
boolean contains(Object o) // 是否包含 o
boolean isEmpty(); // 是否為空
int size(); //集合元素
Iterator<E> iterator(); // 返回迭代器
Object[] toArray(); // 轉為 Object數組
<T> T[] toArray(T[] a); // 轉為具體類型數組
boolean add(E e); // 加入尾部
boolean remove(Object o); // 移除 o
boolean containsAll(Collection<?> c); //是否報考 集合 c
boolean addAll(Collection<? extends E> c);// 合並 c
boolean retainAll(Collection<?> c);//保留只存在集合 c 的元素
void clear(); // 清除集合元素
void sort(Comparator<? super E> c) //根據 Comparator 排序
E get(int index); // 根據下標獲取 元素
E set(int index, E element); // 設置第 index 的元素
E remove(int index); // 移除 第 index 的元素
<E> List<E> of(E e1.....) // jdk 9
List<E> copyOf(Collection<? extends E> coll) // 復制
二、Vector(向量)——ArrayList 線程安全的翻版
底層:數組
特點:查詢快,線程安全,線程安全
Api:
boolean synchronized contains(Object o);
boolean synchronized isEmpty();
boolean synchronized containsAll(Collection<?> c);
boolean addAll(Collection<? extends E> c);
public synchronized boolean add(E e)
public synchronized E get(int index);
public synchronized E set(int index, E element);
public synchronized E firstElement()
public synchronized void removeElementAt(int index)
public synchronized E lastElement()
public synchronized void setElementAt(E obj, int index)
public synchronized E remove(int index)
public void clear()
Iterator<E> iterator();
三、 Stack(棧)——后進先出的線性表
底層:數組
特點:用於模擬"棧"這種數據結構(LIFO后進先出),線程安全,允許 null 值
Api:
public E push(E item) //推入棧頂
public synchronized E pop() // 彈出棧頂元素,不存在則報錯
public synchronized E peek() // 獲取棧頂元素,不移除
public boolean empty() // 棧是否為空
public synchronized int search(Object o) // 搜索元素

四、Map——不屬於Collection的映射關系
底層:key到value的映射
特點:可以使任何引用類型的數據,但key不能重復,無法遍歷( 沒有繼承 Collection 接口)
4.1、TreeMap(1.8JDK)
底層:紅黑樹
特點:不允許出現重復的key,可以插入null鍵,null值,可以對元素進行排序,無序集合(插入和遍歷順序不一致)。

4.2、HashMap
底層:數組+鏈表+紅黑樹,空參的HashMap初始容量是16,默認加載因子為0.75。
特點:具有很快的訪問速度,最多允許一條記錄的鍵為 null,線程不安全,存儲無序。
HashMap如何處理hash沖突:
- 鏈地址法:如果存在 hash 碰撞,則創建一鏈表存儲相同的元素
- 開放定址法:就是一旦發生了沖突,就去尋找下一個空的散列地址,只要散列表足夠大,空的散列地址總能找到,並將記錄存入。
- 再哈希:同時構造多個不同的哈希函數,當哈希地址Hi=RH1(key)發生沖突時,再計算Hi=RH2(key)……,直到沖突不再產生。
- 建立公共溢出區:將哈希表分為基本表和溢出表兩部分,凡是和基本表發生沖突的元素,一律填入溢出表。
4.3、HashTable(基本被淘汰)
特點:HashTable的操作幾乎和HashMap一致,主要的區別在於HashTable為了實現多線程安全,在幾乎所有的方法上都加上了synchronized鎖,而加鎖的結果就是HashTable操作的效率十分低下。
HashMap和HashTable區別:
- HashMap允許有一個鍵為null,允許多個值為null,但HashTable不允許鍵或值為null。
- Hash映射:HashMap的hash算法通過非常規設計,將底層table長度設計為2的冪,使用位與運算代替取模運算,減少運算消耗;而HashTable的hash算法首先使得hash值小於整型數最大值,再通過取模進行散射運算
4.4、LinkedHashMap
底層:HashMap和雙向鏈表合二為一
特點:LinkedHashMap的元素存取過程基本與HashMap基本類似,只是在細節實現上稍有不同。當然,這是由LinkedHashMap本身的特性所決定的,因為它額外維護了一個雙向鏈表用於保持迭代順序。此外,LinkedHashMap可以很好的支持LRU算法。
4.5、WeakHashMap
底層:散列表,它存儲的內容也是鍵值對(key-value)映射
特點:鍵和值都可以是 null,WeakHashMap的鍵是“弱鍵”。在 WeakHashMap 中,當某個 key 不再被強引用使用時,會被從WeakHashMap中被 JVM 自動移除,然后它對應的鍵值對也會被從WeakHashMap中移除。
4.6、ConcurrentHashMap(1.8JDK)
底層:底層數據結構是 數組 + 鏈表/紅黑樹
特點:ConcurrentHashMap 是 HashMap 的多線程安全版本。它使用了細粒度鎖 和 cas 提高了在多線程環境的安全性和高並發
悲觀鎖與樂觀鎖:
synchronized是悲觀鎖,這種線程一旦得到鎖,其他需要鎖的線程就掛起的情況就是悲觀鎖。
CAS操作的就是樂觀鎖,每次不加鎖而是假設沒有沖突而去完成某項操作,如果因為沖突失敗就重試,直到成功為止。
4.7、ConcurrentSkipListMap
底層:基於跳躍鏈表的實現的 map
特點:使用了 cas 技術實現線程安全性,高並發
ConcurrentSkipListMap 相比 ConcurrentHashMap 的優點:
-
ConcurrentSkipListMap 的key是有序的
-
ConcurrentSkipListMap 支持更高的並發。ConcurrentSkipListMap的存取時間是log(N),和線程數幾乎無關。也就是說在數據量一定的情況下,並發的線程越多,ConcurrentSkipListMap 越能體現出它的優勢
4.8、NavigableMap 和 ConcurrentNavigableMap 操作 key 值的范圍區間
原理:
- TreeMap 實現了 NavigableMap 。ConcurrentNavigableMap 高並發線程安全版的 TreeMap
- NavigableMap 提供了針對給定搜索目標返回最接近匹配項的導航方法。
Api:
K lowerKey(K key) // 找到第一個比指定的key小的值
K floorKey(K key) // 找到第一個比指定的key小於或等於的key
K ceilingKey(K key) // 找到第一個大於或等於指定key的值
K higherKey(K key) // 找到第一個大於指定key的值
Map.Entry<K,V> firstEntry() // 獲取最小值
Map.Entry<K,V> lastEntry() // 獲取最大值
Map.Entry<K,V> pollFirstEntry() // 刪除最小的元素
Map.Entry<K,V> pollLastEntry() // 刪除最大的元素
NavigableMap<K,V> descendingMap() //返回一個倒序的Map
// 返回值小於 toKey 的 NavigableMap
NavigableMap<K,V> headMap(K toKey, boolean inclusive)
// 返回值大於 fromKey 的 NavigableMap
NavigableMap<K,V> tailMap(K fromKey, boolean inclusive)
// 返回值小於 toKey 大於 的 fromKey NavigableMap
NavigableMap<K,V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive)
五、Set(集合)——元素唯一的無下標數組
特點:存儲順序無關,元素不可重復,如果加入重復元素,會保留最先加入的對象。存取速度快。但與List不同的是,Set還提供了equals(Object o)和hashCode(),供其子類重寫,以實現對集合中插入重復元素的處理。
Api:
| 方法 | 內容 |
|---|---|
| boolean add(E o) | 如果 set 中尚未存在指定的元素,則添加此元素(可選操作) |
| boolean addAll(Collection<? extends E> c) | 如果 set 中沒有指定 collection 中的所有元素,則將其添加到此 set 中(可選操作) |
| void clear() | 移除 set 中的所有元素(可選操作) |
| boolean contains(Object o) | 如果 set 包含指定的元素,則返回 true |
| boolean containsAll(Collection<?> c) | 如果此 set 包含指定 collection 的所有元素,則返回 true |
| boolean equals(Object o) | 比較指定對象與此 set 的相等性 |
| int hashCode() | 返回 set 的哈希代碼值 |
| boolean isEmpty() | 如果 set 不包含元素,則返回 true |
| Iterator iterator() | 返回在此 set 中的元素上進行迭代的迭代器 |
| boolean remove(Object o) | 如果 set 中存在指定的元素,則將其移除(可選操作) |
| boolean removeAll(Collection<?> c) | 移除 set 中那些包含在指定 collection 中的元素(可選操作)。 |
| boolean retainAll(Collection<?> c) | 僅保留 set 中那些包含在指定 collection 中的元素(可選操作) |
| int size() | 返回 set 中的元素數(其容量) |
| Object[] toArray() | 返回一個包含 set 中所有元素的數組 |
| T[] toArray(T[] a) | 返回一個包含 set 中所有元素的數組;返回數組的運行時類型是指定數組的類型 |
5.1、TreeSet
底層:紅黑樹
特點:具有排序功能(自然排序(123456)和自定義排序,默認是自然排序),允許插入Null值,不允許插入重復元素,線程不安全。

紅黑樹: 一種自平衡二叉查找樹

5.2、HashSet
底層:哈希表(HashMap)
特點:不允許出現重復因素,允許插入Null值,元素無序(添加順序和遍歷順序不一致),線程不安全。
基本操作:插入的元素被當做是HashMap的key,根據hashCode值來確定集合中的位置,由於Set集合中並沒有角標的概念,所以並沒有像List一樣提供get()方法。當獲取HashSet中某個元素時,只能通過遍歷集合的方式進行equals()比較來實現。
5.3、LinkedHashSet
底層:鏈表和哈希表共同實現
特點:鏈表保證了元素的順序與存儲順序一致,哈希表保證了元素的唯一性,效率高,但線程不安全。
5.4、ConcurrentSkipListSet
底層:基於 ConcurrentSkipListMap 實現
5.5、CopyOnWriteArraySet
底層:基於 CopyOnWriteArrayList 實現
5.6、BitSet
底層:long數組
特點:BitSet是位操作的對象,值只有 0 或 1 即false和true,內部維護了一個long數組,初始只有一個long,所以BitSet最小的size是64,當隨着存儲的元素越來越多,BitSet內部會動態擴充,最終內部是由N個long來存儲。
Api:
void and(BitSet set) // 兩個BitSet 做與操作,結果並存入當前 BitSet
void andNot(BitSet set) // 兩個BitSet 與非操作
void flip(int index) // 反轉某一個指定 index
boolean intersects(BitSet bitSet) // 是否有交集
int cardinality() //返回 true/1 的個數
void clear() // 重置
void clear(int startIndex, int endIndex) // startIndex~endIndex 重置
int nextSetBit(int startIndex) //檢索在startIndex之后出現為1的第一位的索引
int nextClearBit(int startIndex) //檢索在startIndex之后出現為0的第一位的索引
5.7、TreeSet和HashSet對比
共同點:允許插入Null值,不允許插入重復元素,線程不安全,可以被克隆,可以被序列化。
不同點:TreeSet對插入的元素進行排序,是一個有序的集合,HashSet無序。TreeSet底層使用紅黑樹,HashSet底層使用HashMap。
六、Queue(隊列)——一端進一端出的線性表
底層:基於數組,鏈表實現
特點:存儲有序
Api:
boolean add(E e); //加入隊列尾部
boolean offer(E e); // 加入隊列尾部,並返回結果
E remove(); //移除頭部元素
E poll(); // 獲取頭部元素,並移除
E element(); // 獲取頭部元素,不存在則報錯
E peek(); // 獲取頭部元素,不移除

6.1、PriorityQueue
底層:PriorityQueue是按優先級排序的隊列,也就是說 vip 可以插隊。優先隊列要求使用 Java Comparable 和 Comparator 接口給對象排序,並且在排序時會按照優先級處理其中的元素。
6.2、PriorityBlockingQueue
底層:線程安全的PriorityQueue。
6.3、BlockingQueue
底層:BlockingQueue很好的解決了多線程中,如何高效安全“傳輸”數據的問題。通過這些高效並且線程安全的隊列類,為我們快速搭建高質量的多線程程序帶來極大的便利。常用於線程的任務隊列
6.4、DelayQueue
DelayQueue是一個沒有邊界BlockingQueue實現,加入元素必須實現Delayed接口。當生產者線程調用put之類的方法加入元素時,會觸發 Delayed 接口中的compareTo方法進行排序。消費者線程查看隊列頭部的元素,注意是查看不是取出。然后調用元素的getDelay方法,如果此方法返回的值小0或者等於0,則消費者線程會從隊列中取出此元素,並進行處理。如果getDelay方法返回的值大於0,則消費者線程阻塞到第一元素過期。
七、Deque(雙向隊列)——兩端均可進出的線性表
特點:Deque接口代表一個"雙端隊列",雙端隊列可以同時從兩端來添加、刪除元素,因此Deque的實現類既可以當成隊列使用、也可以當成棧使用
**Deque 的子類 **:LinkedList,ArrayDeque,LinkedBlockingDeque
Api:
void addFirst(E e); //加入頭部
void addLast(E e); //加入尾部
boolean offerFirst(E e); //加入頭部,並返回結果
boolean offerLast(E e); //加入尾部,並返回結果
E removeFirst(); // 移除第一個元素
E removeLast(); // 移除最后一個元素
E getFirst(); //獲取第一個元素,不存在則報錯
E getLast(); //獲取最后一個元素,不存在則報錯
E pollFirst(); //獲取第一個元素,並移除
E pollLast(); //獲取最后一個元素,並移除
E peekFirst(); //獲取第一個元素
E peekLast(); // 獲取最后一個元素
void push(E e); //加入頭部
E pop(); //彈出頭部元素
八、總結
增刪改查的通用方法:
| 操作方法 | 拋出異常 | 阻塞線程 | 返回特殊值 | 超時退出 |
|---|---|---|---|---|
| 插入元素 | add(e) | put(e) | offer(e) | offer(e, timeout, unit) |
| 移除元素 | remove() | take() | poll() | pull(timeout, unit) |
| 檢查 | element() | peek() | 無 | 無 |

