[Java基礎]collection

Duancf發表於2024-08-14

陣列與集合區別,用過哪些?

陣列和集合的區別:

  • 陣列是固定長度的資料結構,一旦建立長度就無法改變,而集合是動態長度的資料結構,可以根據需要動態增加或減少元素。
  • 陣列可以包含基本資料型別和物件,而集合只能包含物件。
  • 陣列可以直接訪問元素,而集合需要透過迭代器或其他方法訪問元素。

我用過的一些 Java 集合類:

  • ArrayList: 動態陣列,實現了List介面,支援動態增長。
  • LinkedList: 雙向連結串列,也實現了List介面,支援快速的插入和刪除操作。
  • HashMap: 基於雜湊表的Map實現,儲存鍵值對,透過鍵快速查詢值。
  • HashSet: 基於HashMap實現的Set集合,用於儲存唯一元素。
  • TreeMap: 基於紅黑樹實現的有序Map集合,可以按照鍵的順序進行排序。
  • LinkedHashMap: 基於雜湊表和雙向連結串列實現的Map集合,保持插入順序或訪問順序。
  • PriorityQueue: 優先佇列,可以按照比較器或元素的自然順序進行排序。

說說Java中的集合?

image

List是有序的Collection,使用此介面能夠精確的控制每個元素的插入位置,使用者能根據索引訪問List中元素。常用的實現List的類有LinkedList,ArrayList,Vector,Stack。

ArrayList是容量可變的非執行緒安全列表,其底層使用陣列實現。當幾何擴容時,會建立更大的陣列,並把原陣列複製到新陣列。ArrayList支援對元素的快速隨機訪問,但插入與刪除速度很慢。
LinkedList本質是一個雙向連結串列,與ArrayList相比,,其插入和刪除速度更快,但隨機訪問速度更慢。
Set不允許存在重複的元素,與List不同,set中的元素是無序的。常用的實現有HashSet,LinkedHashSet和TreeSet。

HashSet透過HashMap實現,HashMap的Key即HashSet儲存的元素,所有Key都是用相同的Value,一個名為PRESENT的Object型別常量。使用Key保證元素唯一性,但不保證有序性。由於HashSet是HashMap實現的,因此執行緒不安全。
LinkedHashSet繼承自HashSet,透過LinkedHashMap實現,使用雙向連結串列維護元素插入順序。
TreeSet透過TreeMap實現的,新增元素到集合時按照比較規則將其插入合適的位置,保證插入後的集合仍然有序。
Map 是一個鍵值對集合,儲存鍵、值和之間的對映。Key 無序,唯一;value 不要求有序,允許重複。Map 沒有繼承於 Collection 介面,從 Map 集合中檢索元素時,只要給出鍵物件,就會返回對應的值物件。主要實現有TreeMap、HashMap、HashTable、LinkedHashMap、ConcurrentHashMap

HashMap:JDK1.8 之前 HashMap 由陣列+連結串列組成的,陣列是 HashMap 的主體,連結串列則是主要為了解決雜湊衝突而存在的(“拉鍊法”解決衝突),JDK1.8 以後在解決雜湊衝突時有了較大的變化,當連結串列長度大於閾值(預設為 8)時,將連結串列轉化為紅黑樹,以減少搜尋時間
LinkedHashMap:LinkedHashMap 繼承自 HashMap,所以它的底層仍然是基於拉鍊式雜湊結構即由陣列和連結串列或紅黑樹組成。另外,LinkedHashMap 在上面結構的基礎上,增加了一條雙向連結串列,使得上面的結構可以保持鍵值對的插入順序。同時透過對連結串列進行相應的操作,實現了訪問順序相關邏輯。
HashTable:陣列+連結串列組成的,陣列是 HashMap 的主體,連結串列則是主要為了解決雜湊衝突而存在的
TreeMap:紅黑樹(自平衡的排序二叉樹)
ConcurrentHashMap:Node陣列+連結串列+紅黑樹實現,執行緒安全的(jdk1.8以前Segment鎖,1.8以後volatile + CAS 或者 synchronized)

Java中的執行緒安全的集合是什麼?

在 java.util 包中的執行緒安全的類主要 2 個,其他都是非執行緒安全的。

  • Vector:執行緒安全的動態陣列,其內部方法基本都經過synchronized修飾,如果不需要執行緒安全,並不建議選擇,畢竟同步是有額外開銷的。Vector 內部是使用物件陣列來儲存資料,可以根據需要自動的增加容量,當陣列已滿時,會建立新的陣列,並複製原有陣列資料。
  • Hashtable:執行緒安全的雜湊表,HashTable 的加鎖方法是給每個方法加上 synchronized 關鍵字,這樣鎖住的是整個 Table 物件,不支援 null 鍵和值,由於同步導致的效能開銷,所以已經很少被推薦使用,如果要保證執行緒安全的雜湊表,可以用ConcurrentHashMap。

java.util.concurrent 包提供的都是執行緒安全的集合:

併發Map:

  • ConcurrentHashMap:它與 HashTable 的主要區別是二者加鎖粒度的不同,在JDK1.7,ConcurrentHashMap加的是分段鎖,也就是Segment鎖,每個Segment 含有整個 table 的一部分,這樣不同分段之間的併發操作就互不影響。在JDK 1.8 ,它取消了Segment欄位,直接在table元素上加鎖,實現對每一行進行加鎖,進一步減小了併發衝突的機率。對於put操作,如果Key對應的陣列元素為null,則透過CAS操作(Compare and Swap)將其設定為當前值。如果Key對應的陣列元素(也即連結串列表頭或者樹的根元素)不為null,則對該元素使用 synchronized 關鍵字申請鎖,然後進行操作。如果該 put 操作使得當前連結串列長度超過一定閾值,則將該連結串列轉換為紅黑樹,從而提高定址效率。
  • ConcurrentSkipListMap:實現了一個基於SkipList(跳錶)演算法的可排序的併發集合,SkipList是一種可以在對數預期時間內完成搜尋、插入、刪除等操作的資料結構,透過維護多個指向其他元素的“跳躍”連結來實現高效查詢。

併發Set:

ConcurrentSkipListSet:是執行緒安全的有序的集合。底層是使用ConcurrentSkipListMap實現。
CopyOnWriteArraySet:是執行緒安全的Set實現,它是執行緒安全的無序的集合,可以將它理解成執行緒安全的HashSet。有意思的是,CopyOnWriteArraySet和HashSet雖然都繼承於共同的父類AbstractSet;但是,HashSet是透過“雜湊表”實現的,而CopyOnWriteArraySet則是透過“動態陣列(CopyOnWriteArrayList)”實現的,並不是雜湊表。

併發List:

CopyOnWriteArrayList:它是 ArrayList 的執行緒安全的變體,其中所有寫操作(add,set等)都透過對底層陣列進行全新複製來實現,允許儲存 null 元素。即當物件進行寫操作時,使用了Lock鎖做同步處理,內部複製了原陣列,並在新陣列上進行新增操作,最後將新陣列替換掉舊陣列;若進行的讀操作,則直接返回結果,操作過程中不需要進行同步。
併發 Queue:

ConcurrentLinkedQueue:是一個適用於高併發場景下的佇列,它透過無鎖的方式(CAS),實現了高併發狀態下的高效能。通常,ConcurrentLinkedQueue 的效能要好於 BlockingQueue 。
BlockingQueue:與 ConcurrentLinkedQueue 的使用場景不同,BlockingQueue 的主要功能並不是在於提升高併發時的佇列效能,而在於簡化多執行緒間的資料共享。BlockingQueue 提供一種讀寫阻塞等待的機制,即如果消費者速度較快,則 BlockingQueue 則可能被清空,此時消費執行緒再試圖從 BlockingQueue 讀取資料時就會被阻塞。反之,如果生產執行緒較快,則 BlockingQueue 可能會被裝滿,此時,生產執行緒再試圖向 BlockingQueue 佇列裝入資料時,便會被阻塞等待。

併發 Deque:

LinkedBlockingDeque:是一個執行緒安全的雙端佇列實現。它的內部使用連結串列結構,每一個節點都維護了一個前驅節點和一個後驅節點。LinkedBlockingDeque 沒有進行讀寫鎖的分離,因此同一時間只能有一個執行緒對其進行操作
ConcurrentLinkedDeque:ConcurrentLinkedDeque是一種基於連結節點的無限併發連結串列。可以安全地併發執行插入、刪除和訪問操作。當許多執行緒同時訪問一個公共集合時,ConcurrentLinkedDeque是一個合適的選擇。

Collections和Collection的區別

Collection是Java集合框架中的一個介面,它是所有集合類的基礎介面。它定義了一組通用的操作和方法,如新增、刪除、遍歷等,用於操作和管理一組物件。Collection介面有許多實現類,如List、Set和Queue等。
Collections(注意有一個s)是Java提供的一個工具類,位於java.util包中。它提供了一系列靜態方法,用於對集合進行操作和演算法。Collections類中的方法包括排序、查詢、替換、反轉、隨機化等等。這些方法可以對實現了Collection介面的集合進行操作,如List和Set。

集合遍歷的方法有哪些?

在Java中,集合的遍歷方法主要有以下幾種:

普通 for 迴圈: 可以使用帶有索引的普通 for 迴圈來遍歷 List。

List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");

for (int i = 0; i < list.size(); i++) {
    String element = list.get(i);
    System.out.println(element);
}

增強 for 迴圈(for-each迴圈): 用於迴圈訪問陣列或集合中的元素。

List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");

for (String element : list) {
    System.out.println(element);
}

Iterator 迭代器: 可以使用迭代器來遍歷集合,特別適用於需要刪除元素的情況。

List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");

Iterator<String> iterator = list.iterator();
while(iterator.hasNext()) {
    String element = iterator.next();
    System.out.println(element);
}

ListIterator 列表迭代器: ListIterator是迭代器的子類,可以雙向訪問列表並在迭代過程中修改元素。

List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");

ListIterator<String> listIterator= list.listIterator();
while(listIterator.hasNext()) {
    String element = listIterator.next();
    System.out.println(element);
}

使用 forEach 方法: Java 8引入了 forEach 方法,可以對集合進行快速遍歷。

List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");

list.forEach(element -> System.out.println(element));

Stream API: Java 8的Stream API提供了豐富的功能,可以對集合進行函式式操作,如過濾、對映等。

List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");

list.stream().forEach(element -> System.out.println(element));

這些是常用的集合遍歷方法,根據情況選擇合適的方法來遍歷和操作集合。

相關文章