容器--EnumMap
一、概述
EnumMap是一类特殊的Map, 其特殊之处在于KEY需要是枚举类型,由于枚举类型的特点是值的个数是固定的,所以,对于EnumMap来说,其所能存储的个数也就是固定的了。这种类型的Map相对来说是比较简单的。
二、主要实现介绍
1. 初始化
由于EnumMap的enum特点,决定了其容器的容量是不变的,所以,在创建一个EnumMap的时候,我们就需要指定其大小,目前创建一个EnumMap主要有以下几种方式:
public EnumMap(Class<K> keyType) : 根据键的class类型,通过反射的方式得到该枚举的所有可能值,进而生成一个键数组及值数组。
public EnumMap(EnumMap<K, ? extends V> m) :从另一个EnumMap去初始化,本质上是生成一个副本。
public EnumMap(Map<K, ? extends V> m):根据另一个Map初始化,和第二个方法相比,多了个对Map的转化的过程
以上三种初始化方式之后,在内部都会创建一个key数组和value数组,它们的大小是相同的。特别的,由于对同一类enum来说,其key是固定的,所以key数组是可以复用的。
2. 存储
我们来看一下对于put操作的实现
public V put(K key, V value) { typeCheck(key);//类型检查 int index = key.ordinal(); Object oldValue = vals[index]; vals[index] = maskNull(value);//null值处理 if (oldValue == null) size++; return unmaskNull(oldValue); }
可以看到整个实现非常简单,key对应的value被存储在vals数组中,key的ordinal对应的下标的位置。
需要注意的是,在存储时,系统对于值进行了maskNull的处理,在返回时做了unmaskNull处理,我们接着看下相关的代码:
private static final Object NULL = new Object() { public int hashCode() { return 0; } public String toString() { return "java.util.EnumMap.NULL"; } }; private Object maskNull(Object value) { return (value == null ? NULL : value); } private V unmaskNull(Object value) { return (V) (value == NULL ? null : value); }
可以看到这个方法的作用是,如果目标value为null,则用NULL对象来替换,那么为什么要这样做呢?
这是因为,vals数组中,默认是没有值的,而这个用null来表示。那么,EnumMap本身是支持值为null的,如果不做任何处理将vals设置为null,则无法和没有值的情况进行区分,所以借助于这种方式来实现对于null值的表示。
3. 取值
和存值一样,取值也是比较简单的,如下:
public V get(Object key) { return (isValidKey(key) ? unmaskNull(vals[((Enum)key).ordinal()]) : null); }
可见相比较于HashMap,这个取值操作是直接定位的,非常快速。
4. key存在判断
前面说到,初始化时实际上就已经确定了key的数组,那么,是否表示所有的key都存在呢?参见实现:
public boolean containsKey(Object key) { return isValidKey(key) && vals[((Enum)key).ordinal()] != null; }
可见,如果某个key对应的下标没有设置值,系统认为这个key是未被包含的。
三、总结
从前面的分析我们可以看到,EnumMap的实现非常简单,而且存取都非常高效,如果我们的业务场景是可以设置几个固定的key的值的话,那么用这类key将是非常高效的。