java Collection介面和Map介面知識點總結
最近寫程式碼,感覺對jdk中Collection和Map中的實現方式和原理有些遺忘,為了在寫程式碼的時候讓程式碼更加簡介和讓效能更加優秀,所以我先必須瞭解其中容器增長方式和原理,HashMap,如果那個Node桶存放的資料大小大於等於8,就會採用紅黑樹儲存。
工具:starUML、IDEA
jdk版本:jdk1.8.0_102
(一) 先貼上一張類圖:
(二) 其中Collection和Map部分實現中實現引數。
1、 全域性實現的引數:
新容量大小/原容器大小 = N/O
x = 沒有該值
Collection | |||||||||
List | Set | ||||||||
ArrayList | AttributeList | RoleList | LinkedList | Vector | Stack | HashSet | TreeSet | LinkedHashSet | |
實現方式 |
Object[] 陣列 |
Object[] 陣列 |
Object[] 陣列 |
Node<E> 雙向連結串列 |
Object[] 陣列 |
Object[] 陣列 |
HashMap<E,Object> |
NavigableMap<E, Object> | HashMap<E,Object> |
預設容器大小 | 10 | 10 | 10 | x | 10 | 10 | 16 | x | 16 |
擴充容量方式 | System.arraycopy | System.arraycopy | System.arraycopy | x | System.arraycopy | System.arraycopy | (Node<K, V>[])new Node[newCap] | x | (Node<K, V>[])new Node[newCap] |
N/O | 1.5 | 1.5 | 1.5 | x | 2(預設,值在初始化的第二個引數可設定) | 2預設,值在初始化的第二個引數可設定) | 2 | x | 2 |
負載因子 | 1 | 1 | 1 | x | 1 | 1 | 0.75f | x | 0.75f |
預設門閥 | 記載因子*容量大小 | 記載因子*容量大小 | 記載因子*容量大小 | x | 記載因子*容量大小 | 記載因子*容量大小 |
如果使用無參構造方式,預設門閥為“預設載入因子”*"預設cap大小". 如果使用其他建構函式,門閥值初始為 將該初始因子-1的值寫成機器碼,從最高位(從左往右除符號位的第一個1)開始,右邊所有位變1的值。 |
x |
如果使用無參構造方式,預設門閥為“預設載入因子”*"預設cap大小". 如果使用其他建構函式,門閥值初始為 將該初始因子-1的值寫成機器碼,從最高位(從左往右除符號位的第一個1)開始,右邊所有位變1的值。 |
執行緒安全 | 否 | 否 | 否 | 否 | 是 | 是 | 否 | x | 否 |
Map | |||||||||
HashMap | LinkedHashMap | Hashtable | WeakHashMap | TreeMap | EnumMap | Attributes | ConcurrentHashMap | Properties | |
實現方式 |
Node<K, V>[] (根據key的hash值和長度來決定選擇那個桶index)有hashValue,next的連結串列結構(這裡面定義了TreeNode extend LinkedHashMap.Entry) |
LinkedHashMap.Entry<K, V> 雙向連結串列實體,繼承HashMap.Node<K, V> |
Entry<K,V>[] 是HashMap的安全實現 |
Entry<K,V>[] 是繼承WeakReference實現弱引用,減少在記憶體中存活時間,節省記憶體。 |
Entry<K, V> 有right,left,parent的map結構 |
Object [] 陣列 |
Map<Object, Object>,使用的HashMap實現 |
Node<K,V>[] 陣列 |
Entry<K,V>[] 是HashMap的安全實現,是繼承於HashTable |
預設容器大小 | 16 | x | 11 | 16 | x | 動態 | 11 | 16 | 11 |
擴充容量方式 | (Node<K, V>[])new Node[newCap] | x | new Entry<?, ?>[newCapacity] | (Node<K, V>[])new Node<?, ?>[newCap] | x | x | (Node<K, V>[])new Node[newCap] | (Node<K, V>[])new Node<?, ?>[n << 1] | new Entry<?, ?>[n] |
N/O | 2 | x | 2 另外加1 | 2 | x | x | 2 | 2 | 2 另外加1 |
負載因子 | 0.75f | x | 0.75f | 0.75f | x | x | 0.75f | 0.75f | 0.75f |
預設門閥 |
如果使用無參構造方式,預設門閥為“預設載入因子”*"預設cap大小". 如果使用其他建構函式,門閥值初始為 將該初始因子-1的值寫成機器碼,從最高位(從左往右除符號位的第一個1)開始,右邊所有位變1的值。 |
x | 記載因子*容量大小 | 記載因子*容量大小 | x | x |
如果使用其他建構函式,門閥值初始為 將該初始因子-1的值寫成機器碼,從最高位(從左往右除符號位的第一個1)開始,右邊所有位變1的值。 |
如果使用其他建構函式,門閥值初始為 將該初始因子-1的值寫成機器碼,從最高位(從左往右除符號位的第一個1)開始,右邊所有位變1的值。 |
記載因子*容量大小 |
執行緒安全 | 否 | 否 | 是 | 否 | 否 | 否 | 否 | 是 | 是 |
2、執行緒安全建構函式和門閥與N/O的關係詳細說明:
新容量大小/原容器大小 = N/O
新門閥/老門閥 = NT/OT
x = 不存在
i = 輸入值
oldCap = 老容器大小
oldCap/2(取整) = oldCap >> 1
{2^n}= 滿足2的n次方值大於i條件2的n次方的最小值
動態 = 表示它沒有實現,需要傳入一個實現的物件,按照物件來使用
1) 執行緒安全:
Vector | Stack(繼承與Vector) | |||||||||
初始容量 | 負載因子 | 門閥 | N/O | NT/OT | 初始容量 | 負載因子 | 門閥 | N/O | NT/OT | |
預設容器大小 | 10 | x | x | 2 | x | 10 | x | x | 2 | x |
只有initialCapacity引數值 | i | x | x | 2 | x | x | x | x | x | x |
有initialCapacity,loadFactor引數的值 | i | x | x | 這兒沒有loadFactor引數,有capacityIncrement引數,每次增加capacityIncrement | x | x | x | x | x | x |
Stack(繼承與Vector) | Hashtable | ConcurrentHashMap | Properties | |||||||||||||
NT/OT | 初始容量 | 負載因子 | 門閥 | N/O | NT/OT | 初始容量 | 負載因子 | 門閥 | N/O | NT/OT | 初始容量 | 負載因子 | 門閥 | N/O | NT/OT | |
預設容器大小 | x | 11 | 0.75f | 8 | 2 倍另外單獨+1 | 2 | 16 | 0.75f | x | 2 | 2 | 11 | 0.75f | 8 | 2 倍另外單獨+1 | 2 |
只有initialCapacity引數值 | x | i | 0.75f |
i * 0.75f |
2 倍另外單獨+1 | 2 | 3i/2 + 1 | 0.75f |
i * 0.75f |
2 | 2 | i | 0.75f |
i * 0.75f |
2 倍另外單獨+1 | 2 |
有initialCapacity,loadFactor引數的值 | x | i | i | i(容量) *i(負載因子) | 2 倍另外單獨+1 | 2 | {2^i/loadFactor} | i | i(容量) *i(負載因子) | 2 | 2 | i | i | i(容量) *i(負載因子) | 2 倍另外單獨+1 | 2 |
2)執行緒不安全:
ArrayList | AttributeList | RoleList | LinkedList | |||||||||||||||||
初始容量 | 負載因子 | 門閥 | N/O | NT/OT | 初始容量 | 負載因子 | 門閥 | N/O | NT/OT | 初始容量 | 負載因子 | 門閥 | N/O | NT/OT | 初始容量 | 負載因子 | 門閥 | N/O | NT/OT | |
無參建構函式容器大小 | 10 | x | oldCap | 1.5 | x | 10 | x | oldCap | 1.5 | x | 10 | x | oldCap | 1.5 | x | x | x | x | x | x |
只有initialCapacity引數值 | i | x | oldCap | 1.5 | x | i | x | oldCap | 1.5 | x | i | x | oldCap | 1.5 | x | x | x | x | x | x |
有initialCapacity,loadFactor引數的值 | i | x | oldCap | 1.5 | x | i | x | oldCap | 1.5 | x | i | x | oldCap | 1.5 | x | x | x | x | x | x |
HashSet(資料儲存是通過HashMap實現的) | TreeSet | LinkedHashSet(繼承HashSet) | HashMap | |||||||||||||||||
初始容量 | 負載因子 | 門閥 | N/O | NT/OT | 初始容量 | 負載因子 | 門閥 | N/O | NT/OT | 初始容量 | 負載因子 | 門閥 | N/O | NT/OT | 初始容量 | 負載因子 | 門閥 | N/O | NT/OT | |
無參建構函式容器大小 | 16 | 0.75f | 12 | 2 | 2 | x | x | x | x | x | 16 | 0.75f | 12 | 2 | 2 | 16 | 0.75f | 12 | 2 | 2 |
只有initialCapacity引數值 | {2^n} | 0.75f |
{2^n} * 0.75f |
2 | 2 | x | x | x | x | x | {2^n} | 0.75f |
{2^n} * 0.75f |
2 | 2 | {2^n} | 0.75f |
{2^n} * 0.75f |
2 | 2 |
有initialCapacity,loadFactor引數的值 | {2^n} | i |
{2^n} * 0.75f |
2 | 2 | x | x | x | x | x | {2^n} | i |
{2^n} * 0.75f |
2 | 2 | {2^n} | i |
{2^n} * 0.75f |
2 | 2 |
LinkedHashMap | WeakHashMap | TreeMap | EnumMap | Attributes(資料用HashMap來儲存) | ||||||||||||||||||||
初始容量 | 負載因子 | 門閥 | N/O | NT/OT | 初始容量 | 負載因子 | 門閥 | N/O | NT/OT | 初始容量 | 負載因子 | 門閥 | N/O | NT/OT | 初始容量 | 負載因子 | 門閥 | N/O | NT/OT | 初始容量 | 負載因子 | 門閥 | N/O | NT/OT |
16 | 0.75f | 12 | 2 | 2 | 16 | 0.75f | 12 | 2 | 2 | x | x | x | x | x | 動態 | 動態 | 動態 | 動態 | 動態 | 16 | 0.75f |
{2^n} * 0.75f |
2 | 2 |
i | 0.75f |
{2^n} * 0.75f |
2 | 2 | {2^n} | 0.75f |
{2^n} * 0.75f |
2 | 2 | x | x | x | x | x | 動態 | 動態 | 動態 | 動態 | 動態 | {2^n} | 0.75f |
{2^n} * 0.75f |
2 | 2 |
i | i |
{2^n} * 0.75f |
2 | 2 | {2^n} | i |
{2^n} * 0.75f |
2 | 2 | x | x | x | x | x | 動態 | 動態 | 動態 | 動態 | 動態 | x | x | x | x |
補充描述:這裡面有些很複雜,詳細還是得去研究原始碼,而且,每個jdk版本的實現的方式不一樣,建議有時間詳細看原始碼,瞭解其中原理。最重要的是HashMap,TreeMap,Hashtable,ConcurrentHashMap這些詳細檢視很有用, 這個其中還常使用到CAS。
(三) 知識點總結:
HashMap,雖然儲存資料是通過key-value的形式儲存的,但是,它內部為了提高查詢效率,使用的key的hash值,構建陣列物件鏈。先是陣列,稱作“桶”,通過hash值來判斷放那個桶,桶的數量就是上面的cap。每個桶存放的資料小於8個的時候,是採用簡單的物件表,如果那個Node桶存放的資料大小大於等於8,針對該桶的資料就會採用紅黑樹儲存。
ConcurrentHashMap在這個版本實現和HashMap內容差不多,只是增加了執行緒安全的操作,其中載入因子只是在容器初始化的時候和cap通過cap/loadFactor + 1向上取2^n的整數,還有這個版本Segment只有在JAVA物件流進行序列化和反序列化中使用。