探索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在初始化时将枚举类中的所有对象存储到数组中,而后续的增删改查实际上都是对其值对象的操作。
重点关注
内部实现机制
浙公网安备 33010602011771号