1. 程式人生 > >JAVA常用集合實現方式的總結

JAVA常用集合實現方式的總結

    本篇主要是對之前的集合原始碼閱讀系列部落格的一個總結,如果沒有看過之前的部落格或者集合原始碼,很可能看不懂下面的內容。     文章開始之前,首先簡單回顧一下資料結構中定義的物理結構和邏輯結構。     物理結構:
  • 順序儲存結構
  • 鏈式儲存結構
    邏輯結構
  • 線性結構:一般線性表、棧、佇列、字串、陣列、廣義表
  • 非線性結構:樹、圖
關於集合命名規則,Java中集合的命名大體上可以歸類為AbEf的形式:  Ab代表物理結構,Ef代表邏輯結構,但是這只是對於大多數的集合名有效。如ArrayList,就是用陣列實現線性表;而LinkedList就是用連結串列實現的線性表。 最後補充一個必知的native方法:
  • arraycopy()@java.lang.System.class
  • public static native void arraycopy(Object src,  int  srcPos, Object dest, int destPos, 
    int length);
  • 功能:將src中的srcPos~srcPos+length-1的資料複製到dest中的destPos~destPos+length-1位置處;

順序儲存結構實現的集合(即陣列實現)

A、B、C、D是按照底層實現的相似性進行劃分的

PartA-線性表&棧

ArrayList:
  • 內部儲存了一個Object[];提供隨機訪問;
  • ArrayList中資料的移動大多是利用System.arraycopy();方法實現,該方法是native方法;
  • 擴容;
    • 預設大小是10;
    • 最大容量Integer.MAX_VALUE - 8;
    • 擴容觸發條件:當前要求容量大小大於當前容量大小
    • 正常擴容newCapacity = oldCapacity + (oldCapacity >> 1);
    • 使用System.arraycopy將資料全部移動到新的陣列中
  • 縮容:手動呼叫trimToSize()方法,會縮到當前陣列實際儲存的資料大小
Vector:
  • Vector與ArrayList共同點
    • 兩者實現的功能相同,方法的實現也基本一致;
    • 都具有SubList內部類和Itr迭代器內部類
    • 都能夠擴容和縮容
  • Vector與ArrayList區別:
    • 執行緒安全:Vector對大部分的方法使用了關鍵字Synchronized宣告
    • 額外的%element%方法:這些方法會被Stack類所使用;
Stack:
  • Stack主要使用的是Vector<E>的方法進行具體的實現,自身沒有狀態量
  • 執行緒安全:繼承Vector,故也是執行緒安全的
  • pop方法:使用elementAt+removeElementAt
  • push方法:使用addElement
  • search方法:從後往前搜尋呼叫lastIndexOf

PartB-佇列

ArrayDeque:
  • 儲存有一個Obejct[]陣列;陣列長度必須是2的冪次,因為這樣就方便取餘運算;
  • 有一個head和tail指標;
  • tail =(tail+1)&(elements.length-1)
  • head =(head+1)&(elements.length-1)
  • size = (tail-head)&(elements.length-1)
  • 擴容:
    • 正常擴容後大小為原陣列長度2倍
    • 利用System.arraycopy方法實現
PriorityQueue:
  • 類中儲存有一個Object[]陣列
  • size為當前陣列中儲存的資料的大小
  • 存入的資料型別要求實現了Comparable介面
  • 資料的新增和刪除操作伴隨著調堆的操作;
  • 具有擴容功能:
    • 擴容大小:當前大小的2倍或者1.5倍
    • 擴容條件:準備存入的資料量大於當前Obejct陣列的長度;

PartC-Map

HashMap:
  • 內部儲存了一個Node<K,V>[] table;
  • 內部有定義一個Node內部類和TreeNode內部類;後者是一顆紅黑樹
  • 陣列的下標(雜湊值)等於hash(key)&n-1;
  • 會自動擴容:
    • 預設大小是16;大小必須為2的冪
    • 最大容量是1<<30
    • 填裝因子為0.75
    • 擴容觸發條件:一旦當前儲存資料大於門限(容量*填裝因子)
    • 正常擴容newCapacity = oldCapacity<<1;
    • 每個桶中的資料分成兩撥一波繼續留在原雜湊值下面,一波移動到原雜湊值+oldCapacity位置處
  • 不能自動減容;
  • 紅黑樹的利用:
    • 當一個桶中的元素大於某個固定值,則將該桶中的資料由連結串列儲存結構,轉換成紅黑樹儲存結構;
    • 在此之後的所有操作交由紅黑樹進行處理;
    • 在擴容的時候,對紅黑樹進行拆分的過程中,如果最後元素個數小於固定值,則將紅黑樹儲存結構轉換為普通連結串列儲存結構
HashTable:
  • 基本與HashMap類似;
  • 執行緒安全:HashTable的大部分方法使用了Synchronized關鍵字進行標記
  • 沒有紅黑樹:HashTable對桶中資料的儲存沒有使用紅黑樹,因此效率相對於hashmap會差一點
  • 不支援儲存null:會丟擲異常
HashSet:
  • 內部儲存了一個HashMap<E,Object>變數
  • 一個簡單版本的HashMap;
  • 只使用了key而沒有使用value部分,value永遠存的都是固定的Object

PartD-String

String
  • 內部儲存的是一個final char[]字元陣列;
  • 因此不能修改它指向的字元陣列
StringBuidler
  • 內部儲存的是一個char[]字元陣列
  • 因此可以修改裡面的字元
  • 對於陣列的拷貝大多依賴於System.arraycopy的方法
  • 擴容
    • 預設大小為16
    • 容量最大值:Integer.MAX_VALUE
    • 正常情況擴容大小:int newCapacity = value.length * 2 + 2;
    • 擴容觸發條件:準備儲存的字元數大於當前陣列的長度
  • 縮容:
    • 為當前儲存資料的大小
StringBuffer
  • 與Stringbuilder類似
  • 執行緒安全:方法大多使用了Synchronized關鍵字標記

鏈式儲存結構實現的集合(即連結串列實現)

A、B是按照底層實現的相似性進行劃分的

PartA-Map

TreeMap:
  • 定義了一個內部類Entry<K,V>;該節點定義了這課紅黑樹的儲存結構;
  • 儲存有該樹的根節點;要刪除這顆樹只需要將root指向null即可;GC之後便會回收該紅黑樹,造成其它節點的引用不可達
  • 每次插入、刪除一個樹中節點時;都需要對這顆紅黑樹進行一定的調整;
TreeSet:
  • 內部儲存了一個TreeSet<E,Object>物件
  • 一個簡單版本的TreeMap;
  • 只使用了key而沒有使用value部分,value永遠存的都是固定的Object
PartB-List&Map LinkedList:
  • 內部儲存一個雙向連結串列;所有的操作都是對該連結串列進行操作 
LinkedHashMap:
  • 資料儲存是HashMap只是有在其上面加了一個連結串列結構,每次對該HashMap中的數訪問一次都會改變連結串列結構;保證連結串列中的最後面的資料是最近剛被訪問的那個節點的資料。具有LRU演算法特性