1. 程式人生 > 實用技巧 >探索EnumMap底層實現

探索EnumMap底層實現

前言

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在初始化時將列舉類中的所有物件儲存到陣列中,而後續的增刪改查實際上都是對其值物件的操作。

重點關注

內部實現機制