探索EnumMap底層實現
阿新 • • 發佈:2020-12-22
前言
EnumMap初次見面,請多多關照!對於該類的註釋直接上總結:
專門用於
列舉型別
的鍵的Map實現。EnumMap內部的資料結構是陣列,按列舉常量的宣告順序排列它的鍵,與其他Map實現類不同的是,它的迭代器並不會丟擲快速失敗錯誤!
該類的程式碼不到1000行,速速解決掉,探索EnumMap底層實現是基於JDK1.8
。
資料結構
public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> implements java.io.Serializable, Cloneable { /** * 列舉型別的類型別 * 為什麼需要類型別呢? * 因為在一開始初始化時,EnumMap就會將列舉類的所有物件載入到陣列中,所以每次新增節點時,實際上只是添加了值物件而已 */ private final Class<K> keyType; /** * 包含列舉類的所有物件 */ private transient K[] keyUniverse; /** * 儲存值物件 */ private transient Object[] vals; /** * 節點個數 * vals陣列中儲存的值物件個數 */ private transient int size = 0; /** * 快取entrySet方法的返回值 */ private transient Set<Map.Entry<K,V>> entrySet; }
建構函式
/** * 初始化鍵陣列與值陣列 * 這個就是上面我們所說將列舉型別的所有物件儲存到鍵陣列中 * @param keyType 列舉型別的類型別 */ public EnumMap(Class<K> keyType) { this.keyType = keyType; keyUniverse = getKeyUniverse(keyType);//該方法會將列舉類的所有物件按照宣告的順序存放 vals = new Object[keyUniverse.length]; } /** * 指定EnumMap來初始化 * @param m EnumMap物件 */ public EnumMap(EnumMap<K, ? extends V> m) { keyType = m.keyType; keyUniverse = m.keyUniverse; vals = m.vals.clone(); size = m.size; } /** * 指定集合來初始化 * @param m 指定集合 */ public EnumMap(Map<K, ? extends V> m) { if (m instanceof EnumMap) { EnumMap<K, ? extends V> em = (EnumMap<K, ? extends V>) m; keyType = em.keyType; keyUniverse = em.keyUniverse; vals = em.vals.clone(); size = em.size; } else { if (m.isEmpty()) throw new IllegalArgumentException("Specified map is empty"); keyType = m.keySet().iterator().next().getDeclaringClass(); keyUniverse = getKeyUniverse(keyType); vals = new Object[keyUniverse.length]; putAll(m); } }
簡單方法
/** * 倘若值為null則採用NULL_KEY作為值 * 正如方法名一樣,隱藏Null * @param value 指定鍵 * @return NULL_KEY或指定值 */ private Object maskNull(Object value) { return (value == null ? NULL : value); } /** * 倘若值為NULL_KEY則返回null * 正如方法名一樣,揭露Null * @param value 值 * @return null或指定值 */ @SuppressWarnings("unchecked") private V unmaskNull(Object value) { return (V)(value == NULL ? null : value); } /** * 獲取節點個數 * @return 節點個數 */ public int size() { return size; } /** * 陣列中是否包含指定值 * @param value 指定值 * @return 是否包含指定值 */ public boolean containsValue(Object value) { value = maskNull(value); for (Object val : vals) if (value.equals(val)) return true; return false; } /** * 陣列中是否包含指定鍵 * @param key 指定鍵 * @return 是否包含指定鍵 */ public boolean containsKey(Object key) { return isValidKey(key) && vals[((Enum<?>)key).ordinal()] != null; } /** * 指定鍵是否有效 * 是否符合指定的類型別 * @param key 指定鍵 * @return 是否有效 */ private boolean isValidKey(Object key) { if (key == null) return false; Class<?> keyClass = key.getClass(); return keyClass == keyType || keyClass.getSuperclass() == keyType; //keyClass.getSuperclass這個判斷語句沒有什麼意義,列舉類既不能繼承其他類,也不能被繼承,兩個類就無法發生關係,那這個判斷結果只會是false } /** * 陣列中是否包含指定鍵值對 * @param key 指定鍵 * @param value 指定值 * @return 是否包含鍵值對 */ private boolean containsMapping(Object key, Object value) { return isValidKey(key) && maskNull(value).equals(vals[((Enum<?>)key).ordinal()]); } /** * 指定鍵獲取值 * @param key 指定鍵 * @return null或值 */ public V get(Object key) { return (isValidKey(key) ? unmaskNull(vals[((Enum<?>)key).ordinal()]) : null); } /** * 新增鍵值對 * 可能會發生替換 * @param key 指定鍵 * @param value 指定值 * @return null或舊值 */ public V put(K key, V value) { typeCheck(key); int index = key.ordinal(); Object oldValue = vals[index]; vals[index] = maskNull(value); if (oldValue == null) size++; return unmaskNull(oldValue); } /** * 指定鍵移除值物件 * @param key 指定鍵 * @return null或舊值 */ public V remove(Object key) { if (!isValidKey(key)) return null; int index = ((Enum<?>)key).ordinal(); Object oldValue = vals[index]; vals[index] = null; if (oldValue != null) size--; return unmaskNull(oldValue); } /** * 指定鍵值對移除值物件 * @param key 指定鍵 * @param value 指定值 * @return 是否移除值物件成功 */ private boolean removeMapping(Object key, Object value) { if (!isValidKey(key)) return false; int index = ((Enum<?>)key).ordinal(); if (maskNull(value).equals(vals[index])) { vals[index] = null; size--; return true; } return false; } /** * 批量新增集合 * @param m 指定集合 */ public void putAll(Map<? extends K, ? extends V> m) { //列舉類之間無法繼承,所以這裡壓根就只能指定K、V if (m instanceof EnumMap) { EnumMap<?, ?> em = (EnumMap<?, ?>)m; if (em.keyType != keyType) { if (em.isEmpty()) return; throw new ClassCastException(em.keyType + " != " + keyType); } for (int i = 0; i < keyUniverse.length; i++) { Object emValue = em.vals[i]; if (emValue != null) { if (vals[i] == null) size++; vals[i] = emValue; } } } else { super.putAll(m); } } //中間省略了相關迭代器...較為簡單相似,有興趣的讀者可自行檢視 /** * 清空值陣列 */ public void clear() { Arrays.fill(vals, null); size = 0; } /** * 獲取列舉類中所有物件的陣列 * @param keyType 列舉類型別 * @return 包含所有物件的陣列 */ private static <K extends Enum<K>> K[] getKeyUniverse(Class<K> keyType) { return SharedSecrets.getJavaLangAccess().getEnumConstantsShared(keyType); }
總結
-
專門用於其鍵為
列舉型別
的Map實現。 -
EnumMap的資料結構是陣列,按列舉常量的宣告順序進行排列。
-
EnumMap的鍵不允許為空,值允許為空。
-
EnumMap的迭代器不會發生快速失敗。
-
EnumMap有序、不可重複、非執行緒安全。
-
EnumMap在初始化時將列舉類中的所有物件儲存到陣列中,而後續的增刪改查實際上都是對其值物件的操作。
重點關注
內部實現機制