探索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在初始化时将枚举类中的所有对象存储到数组中,而后续的增删改查实际上都是对其值对象的操作。

重点关注

内部实现机制

posted @ 2020-12-21 22:09  zliawk  阅读(83)  评论(0)    收藏  举报