Java集合知識匯總


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) // 搜索元素 

image-20211110190612681

四、Map——不屬於Collection的映射關系

底層:key到value的映射

特點:可以使任何引用類型的數據,但key不能重復,無法遍歷( 沒有繼承 Collection 接口)

4.1、TreeMap(1.8JDK)

底層:紅黑樹

特點:不允許出現重復的key,可以插入null鍵,null值,可以對元素進行排序,無序集合(插入和遍歷順序不一致)。

img

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值,不允許插入重復元素,線程不安全。

image-20211111111501603

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

img

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(); // 獲取頭部元素,不移除

image-20211111164316977

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()

20200318130238448


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM