java集合框架基礎總結

M_ _yu發表於2020-11-18

集合

List : 儲存的元素有序 可重複
Set: 儲存的元素是無序的,不可重複的
Map: 使用鍵值對進行儲存(key-value);key是無序的不可重複的,value是無序的,可重複的。

List

  • Arraylist
    ArrayList底層是基於陣列實現的,是一個動態陣列,自動擴容。
    ArrayList不是執行緒安全的,只能用在單執行緒環境下。
    實現了Serializable介面,因此它支援序列化,能夠通過序列化傳輸;
    實現了RandomAccess介面,支援快速隨機訪問,實際上就是通過下標序號進行快速訪問;
    實現了Cloneable介面,能被克隆。

  • LinkedList

    ​ LinkedList 是一個繼承於AbstractSequentialList的雙向連結串列。
    ​ LinkedList 實現 List 介面,能對它進行佇列操作。
    ​ LinkedList 實現 Deque 介面,即能將LinkedList當作雙端佇列使用。
    ​ LinkedList 實現了Cloneable介面,即覆蓋了函式clone(),能克隆。
    ​ LinkedList 實現java.io.Serializable介面,這意味著LinkedList支援序列化,能通過序列化去傳輸。
    ​ LinkedList 是非同步的。

  • VectorObject[]陣列

    ​ Vector 是 List 的古老實現類,底層使用 Object[ ]儲存,執行緒安全的

重點:ArrayList擴容機制

  1. ArrayList 的建構函式ArrayList有三種方式來初始化
    以無引數構造方法建立 ArrayList 時,實際上初始化賦值的是一個空陣列
    帶初始容量引數的建構函式。(使用者自己指定容量)
    構造包含指定collection元素的列表,這些元素利用該集合的迭代器按順序返回

  2. add 方法 首先呼叫了ensureCapacityInternal(size + 1),就一定會進過ensureExplicitCapacity() 方法,ArrayList擴容的核心方法grow();

    //得到最小擴容量
    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        	minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        ensureExplicitCapacity(minCapacity);
    }
    
    //判斷是否需要擴容
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)	grow(minCapacity);
    }
    
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    /**
    * ArrayList擴容的核心方法。
    */
    private void grow(int minCapacity) {
    	// oldCapacity為舊容量,newCapacity為新容量
    	int oldCapacity = elementData.length;
    	//將oldCapacity 右移一位,其效果相當於oldCapacity /2,(右移n位等於原來的數值除以2的n次方)
    	int newCapacity = oldCapacity + (oldCapacity >> 1);
    	if (newCapacity - minCapacity < 0)	newCapacity = minCapacity;
    	if (newCapacity - MAX_ARRAY_SIZE > 0)	newCapacity = hugeCapacity(minCapacity);
    	elementData = Arrays.copyOf(elementData, newCapacity);
    }
    

    所以 ArrayList 每次擴容之後容量都會變為原來的 1.5 倍左右
    提示:向 ArrayList 新增大量元素之前最好先使用ensureCapacity 方法,以減少增量重新分配的次數

Map

  • HashMap

    HashMap 是一個雜湊表,它儲存的內容是鍵值對(key-value)對映。
    HashMap 繼承於AbstractMap,實現了Map、Cloneable、java.io.Serializable介面。
    HashMap 的實現不是同步的,這意味著它不是執行緒安全的。它的key、value都可以為null。此外,HashMap中的對映不是有序的。
    HashMap 的例項有兩個引數影響其效能:“初始容量” 和 “載入因子”。當雜湊表中的條目數超出了載入因子與當前容量的乘積時,則要對該雜湊表進行 rehash 操作(即重建內部資料結構),從而雜湊表將具有大約兩倍的桶數。通常,預設載入因子是 0.75, 這是在時間和空間成本上尋求一種折衷。
    JDK1.8 之前 HashMap 由陣列+連結串列組成的,陣列是 HashMap 的主體,連結串列則是主要為了解決雜湊衝突而存在的(“拉鍊法”解決衝突)。JDK1.8 以後在解決雜湊衝突時有了較大的變化,當連結串列長度大於閾值(預設為 8)(將連結串列轉換成紅黑樹前會判斷,如果當前陣列的長度小於 64,那麼會選擇先進行陣列擴容,而不是轉換為紅黑樹)時,將連結串列轉化為紅黑樹,以減少搜尋時間。HashMap 可提供高效的增刪改查操作。
    HashMap 預設的初始化大小為 16。之後每次擴充,容量變為原來的 2 倍。

  • LinkedHashMapLinkedHashMap 繼承自 HashMap,所以它的底層仍然是基於拉鍊式雜湊結構即由陣列和連結串列或紅黑樹組成。另外,LinkedHashMap 在上面結構的基礎上,增加了一條雙向連結串列,使得上面的結構可以保持鍵值對的插入順序。同時通過對連結串列進行相應的操作,實現了訪問順序相關邏輯。通過維護一條雙向連結串列,實現了雜湊資料結構的有序遍歷。

  • Hashtable: 陣列+連結串列組成的,陣列是 HashMap 的主體,連結串列則是主要為了解決雜湊衝突而存在的。執行緒安全,Hashtable(同一把鎖) :使用 synchronized 來保證執行緒安全,效率非常低下。當一個執行緒訪問同步方法時,其他執行緒也訪問同步方法,可能會進入阻塞或輪詢狀態,如使用 put 新增元素,另一個執行緒不能使用 put 新增元素,也不能使用 get,競爭會越來越激烈效率越低。

  • TreeMap: 紅黑樹(自平衡的排序二叉樹)

重點:HashMap擴容機制

  1. HashMap的構造方法有四種,前三種區別都是在於指定初始容量以及負載因子,最後構造器就是把另一個Map的值對映到當前新的Map中
  2. 首次Put鍵值對的時候會先計算對應Key的hash值通過hash值來確定存放的地址。
    緊接著呼叫了putVal方法,在剛剛初始化之後的table值為null因此程式會進入到resize()方法中。而resize方法就是用來進行擴容的。
    HashMap擴容可以分為三種情況:
    第一種:使用預設構造方法初始化HashMap。從前文可以知道HashMap在一開始初始化的時候會返回一個空的table,並且thershold為0。因此第一次擴容的容量為預設值DEFAULT_INITIAL_CAPACITY也就是16。同時threshold = DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR = 12。
    第二種:指定初始容量的構造方法初始化HashMap。可以看到初始容量會先減一,經過或與位移運算後等於2的冪次方,接著threshold = 當前的容量(threshold) * DEFAULT_LOAD_FACTOR。
    第三種:HashMap不是第一次擴容。如果HashMap已經擴容過的話,那麼每次table的容量以及threshold量為原有的兩倍。
  3. 擴容後得到了一個table的節點(Node)陣列,接著根據傳入的hash值去獲得一個對應節點p並去判斷是否為空,是的話就存入一個新的節點(Node)。反之如果當前存放的位置已經有值了就會進入到else中去。接著根據前面得到的節點p的hash值以及key跟傳入的hash值以及引數進行比較,如果一樣則替覆蓋。如果存在Hash碰撞就會以連結串列的形式儲存,把當前傳進來的引數生成一個新的節點儲存在連結串列的尾部(JDK1.7儲存在首部)。而如果連結串列的長度大於8那麼就會以紅黑樹的形式進行儲存。

Set

  • HashSet(無序,唯一): 基於 HashMap 實現的,底層採用 HashMap 來儲存元素
  • LinkedHashSetLinkedHashSetHashSet 的子類,並且其內部是通過 LinkedHashMap 來實現的。有點類似於我們之前說的 LinkedHashMap 其內部是基於 HashMap 實現一樣,不過還是有一點點區別的
  • TreeSet(有序,唯一): 紅黑樹(自平衡的排序二叉樹)

ConcurrentHashMap 執行緒安全的具體實現方式/底層具體實現

​ ConcurrentHashMap 取消了 Segment 分段鎖,採用 CAS 和 synchronized 來保證併發安全。資料結構跟 HashMap1.8 的結構類似,陣列+連結串列/紅黑二叉樹。Java 8 在連結串列長度超過一定閾值(8)時將連結串列(定址時間複雜度為 O(N))轉換為紅黑樹(定址時間複雜度為 O(log(N)))
​ synchronized 只鎖定當前連結串列或紅黑二叉樹的首節點,這樣只要 hash 不衝突,就不會產生併發,效率又提升 N 倍。

相關文章