继承体系
EnumMap是Map体系中的一员,继承AbstractMap父类与Map接口,实现Enum接口。
public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V>
implements java.io.Serializable, Cloneable{}
查看其类声明定义,可以发现EnumMap的key要求必须是一个Enum类型。
重要变量
// key的Enum类型
private final Class<K> keyType;
// 所有k归纳为数组
private transient K[] keyUniverse;
// 所有value值归纳为数组
private transient Object[] vals;
// 当前存储的枚举量
private transient int size = 0;
由于EnumMap存储的是一个enum的数据,因此需要感知到Enum类型,在存储上由于Enum数据是确定的,可以使用确定的数组稳定存储。
构造函数
public EnumMap(Class<K> keyType) {
this.keyType = keyType;
keyUniverse = getKeyUniverse(keyType);
vals = new Object[keyUniverse.length];
}
public EnumMap(EnumMap<K, ? extends V> m) {
keyType = m.keyType;
keyUniverse = m.keyUniverse;
vals = m.vals.clone();
size = m.size;
}
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);
}
}
private static <K extends Enum<K>> K[] getKeyUniverse(Class<K> keyType) {
return SharedSecrets.getJavaLangAccess()
.getEnumConstantsShared(keyType);
}
EnumMap的构造器有三个,本质上最核心的就是求出枚举真实类型以及k数组与v数组。getKeyUniverse方法可以帮助我们从一个枚举获得其枚举原始类型。
重要方法
put方法
public V put(K key, V value) {
// 检查key是否合法
typeCheck(key);
// 获得枚举序号
int index = key.ordinal();
// 从序号获得旧值
Object oldValue = vals[index];
// 如果为null则引用内部NULL对象作为该位置的值
vals[index] = maskNull(value);
// 如果旧值为空(初始化之后在该位置没有赋值),逻辑上size加一
if (oldValue == null)
size++;
// 将旧值转换为真实值对外抛出
return unmaskNull(oldValue);
}
private void typeCheck(K key) {
Class<?> keyClass = key.getClass();
// 传入key的类型不是当前目标存储的枚举类型的场合下将抛出异常
if (keyClass != keyType && keyClass.getSuperclass() != keyType)
throw new ClassCastException(keyClass + " != " + keyType);
}
private V unmaskNull(Object value) {
return (V)(value == NULL ? null : value);
}
private Object maskNull(Object value) {
return (value == null ? NULL : value);
}
put方法相比HashMap要简单的多,可以直接使用enum#ordinal来确定其在数组中的位置
get方法
public V get(Object key) {
return (isValidKey(key) ?
unmaskNull(vals[((Enum<?>)key).ordinal()]) : null);
}
get方法将传入的key转换为enum然后获得其ordinal,进而在数据数组中获得对应的值
remove方法
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);
}
remove方法代码逻辑与get相似,都是先确定了传入key的位置,然后进行清理操作
总结
EnumMap存储了一个枚举的数据,其内部存在枚举真实类型的引用,与对应的枚举数组。由于枚举是确定的,因此枚举数组也是确定的。传入的枚举key对象可以通过其ordinal()序号来确定其数组的位置。