EnumMap 原理相关

问:简单谈谈你对 EnumMap 的理解及其特点与应用场景?

答:EnumMap 是对 Map 接口的实现类,其 key-value 映射中的 key 是 Enum 类型,其原理是一个对象数组,数组的下标索引就是根据 Map 中的 key 直接获取(即枚举中的 ordinal 值),数组长度就是枚举类成员个数;当 key 为枚举类型时其效率比 HashMap 高,因为可以直接获取数组下标索引访问到元素;此外 EnumMap 是保证顺序的,输出是按照 key 在枚举中的顺序来确定的。

大多数时候使用 EnumMap 的场景都是 key-value 对存储中 key 为枚举类型的场景,不过需要注意 EnumMap 的构造方法,如下:

//需要传递一个类型信息,因为没有这个类信息就不知道具体的枚举类是什么,
// 也就无法初始化内部的数据结构。
        public EnumMap(Class < K > keyType)
// 其他构造方法。
        public EnumMap(EnumMap < K, ? extends V > m ) 
        public EnumMap(Map < K, ? extends V > m )

所以 EnumMap 与 HashMap 的主要不同就是构造方法需要传递类型参数,此外 EnumMap 能依据 key 的枚举顺序保证有序性,当 key 为枚举类型时使用 EnumMap 的效率远远高于 HashMap。

问:简单说说 EnumMap 的实现原理?

答:EnumMap 的实现原理依赖内部两个长度相同的数组,一个表示所有可能的键,一个表示对应的值,当放入 key-value 时首先会检查键的类型,如果类型不对会抛出异常,否则调用 key 的 ordinal 获取索引 index,并将值 value 放入值数组 vals[index] 中(注意:如果值 value 为 null,则为了区别真正的 null 与没有值,EnumMap 会将 null 值包装成一个特殊的对象)。

其构造方法主要就是在初始化相关数组,如下:

        public class EnumMap, V> extends AbstractMap implements java.io.Serializable, Cloneable {
            //key键的具体枚举类型
            private final Class keyType;
            // key的所有枚举值
            private transient K[] keyUniverse;
            // EnumMap的存储实现,仅仅为一个枚举成员个数长度的数组 
            private transient Object[] vals;

            ......

            // 构造方法
            public EnumMap(Class keyType) {
                // key的枚举类型赋值
                this.keyType = keyType;
                // 获取枚举类的所有枚举值存入数组缓存使用
                keyUniverse = getKeyUniverse(keyType);
                // 实例化枚举值个数长度的数组 
                vals = new Object[keyUniverse.length];
            }
             ......
        }

其 put 操作的源码如下:

        public V put (K key, V value )
        {
            //检查key类型是不是构造时的类型
            typeCheck(key);
            // 获取枚举成员在枚举中的顺序位置
            int index = key.ordinal();
            // 放值或者替换 //(注意:这里如果value为null这会进行包装为Object和解包装操作) 
            Object oldValue = vals[index];
            vals[index] = maskNull(value);
            if (oldValue == null) size++;
            return unmaskNull(oldValue);
        }
        // value为null时包装成重写过toString和hashCode方法的Object 
        private Object maskNull (Object value ){
            return (value == null ? NULL : value);
        }
        // value为null时从重写过toString和hashCode方法的Object解包装为null
        private V unmaskNull (Object value ){
            return (V) (value == NULL ? null : value);
        }

其获取指定 key 的 value 的 get 方法实现如下:

    //注意这里如果value为null这会进解包装操作,参见put 
        public V get (Object key ){
            return (isValidKey(key) ? unmaskNull(vals[((Enum) key).ordinal()]) : null);
        } 

你可能感兴趣的:(EnumMap 原理相关)