EnumMap 源码分析

Posted zhuxudong

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了EnumMap 源码分析相关的知识,希望对你有一定的参考价值。

EnumMap

1)EnumMap【枚举映射】中的键值必须来自单个枚举。
2)EnumMap 根据枚举键的自然顺序来维护,迭代遍历是有序的。
3)不允许使用 null 键,允许使用 null 值,内部会进行 mask 处理。

创建实例

    /**
     * 枚举键的 Class 类型
     */
    private final Class<K> keyType;

    /**
     * 枚举键的所有有效枚举值组成的数组
     */
    private transient K[] keyUniverse;

    /**
     * 底层存储值的对象数组
     */
    private transient Object[] vals;

    /**
     * 映射总数
     */
    private transient int size = 0;

    /**
     * 通过指定的键枚举类型创建空的枚举映射
     */
    public EnumMap(Class<K> keyType) {
        this.keyType = keyType;
        // 缓存所有的枚举值数组
        keyUniverse = EnumMap.getKeyUniverse(keyType);
        // 创建存储值的对象数组
        vals = new Object[keyUniverse.length];
    }

添加元素

    private static final Object NULL = new Object() {
        @Override
        public int hashCode() {
            return 0;
        }

        @Override
        public String toString() {
            return "java.util.EnumMap.NULL";
        }
    };

    private Object maskNull(Object value) {
        return value == null ? NULL : value;
    }

    @SuppressWarnings("unchecked")
    private V unmaskNull(Object value) {
        return (V)(value == NULL ? null : value);
    }

    /**
     * 往枚举映射中添加元素
     */
    @Override
    public V put(K key, V value) {
        typeCheck(key);

        // 获取键的自然顺序
        final int index = key.ordinal();
        // 读取旧值
        final Object oldValue = vals[index];
        // 写入新值
        vals[index] = maskNull(value);
        // 如果之前为该 slot 为空
        if (oldValue == null) {
            // 则增加计数值
            size++;
        }
        // 返回 旧值
        return unmaskNull(oldValue);
    }

读取元素

    /**
     * 根据目标键读取值
     */
    @Override
    public V get(Object key) {
        /**
         * 1)键合法则读取指定索引处的值,并 mask 后返回
         * 2)键非法则返回 null
         */
        return isValidKey(key) ?
                unmaskNull(vals[((Enum<?>)key).ordinal()]) : null;
    }

    /**
     * 校验键是否合法
     */
    private boolean isValidKey(Object key) {
        // 枚举映射的键不能为 null
        if (key == null) {
            return false;
        }

        // Cheaper than instanceof Enum followed by getDeclaringClass
        // 校验键的类型是否合法
        final Class<?> keyClass = key.getClass();
        return keyClass == keyType || keyClass.getSuperclass() == keyType;
    }

移除元素

    /**
     * 1)如果指定的枚举键存在,则移除并返回旧值
     * 2)否则返回 null
     */
    @Override
    public V remove(Object key) {
        // 1)键非法则返回 null
        if (!isValidKey(key)) {
            return null;
        }
        // 读取枚举键的自然顺序
        final int index = ((Enum<?>)key).ordinal();
        // 读取旧值
        final Object oldValue = vals[index];
        // 置空
        vals[index] = null;
        if (oldValue != null) {
            // 如果存在旧值,则递减元素总个数
            size--;
        }
        return unmaskNull(oldValue);
    }

是否包含指定键

    /**
     * 枚举映射包含指定的键
     */
    @Override
    public boolean containsKey(Object key) {
        // 键合法并且以目标键的自然顺序为索引,读取 vals 中的值不为 null
        return isValidKey(key) && vals[((Enum<?>)key).ordinal()] != null;
    }

是否包含指定值

    /**
     * 值数组中是否包含指定的值
     */
    @Override
    public boolean containsValue(Object value) {
        value = maskNull(value);

        for (final Object val : vals) {
            if (value.equals(val)) {
                return true;
            }
        }

        return false;
    }

以上是关于EnumMap 源码分析的主要内容,如果未能解决你的问题,请参考以下文章

Android 逆向整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 | /bin/dexopt 源码分析 )(代码片段

Android 事件分发事件分发源码分析 ( Activity 中各层级的事件传递 | Activity -> PhoneWindow -> DecorView -> ViewGroup )(代码片段

《Docker 源码分析》全球首发啦!

mysql jdbc源码分析片段 和 Tomcat's JDBC Pool

Android 逆向ART 脱壳 ( DexClassLoader 脱壳 | DexClassLoader 构造函数 | 参考 Dalvik 的 DexClassLoader 类加载流程 )(代码片段

Android 逆向ART 脱壳 ( DexClassLoader 脱壳 | DexClassLoader 构造函数 | 参考 Dalvik 的 DexClassLoader 类加载流程 )(代码片段