1、EnumMap-源码分析-底层数据结构
1、EnumMap-源码分析-底层数据结构:
1-1、底层使用数组实现(K,V 双数组),其key必须为Enum类型,两个数组,一个数组keyUniverse存储key,另一个数组vals存储val,两个数组通过下标对应起来。
1-2、key 数组会在构造函数中根据 keyType 进行初始化,当 EnmumMap的value为 null 时会特殊处理为一个 Object 对象。
2、EnumMap-主要属性有:
/**
* The <tt>Class</tt> object for the enum type of all the keys of this map.
*
* 所有kye的枚举类型的<tt>类</tt>对象
* @serial:连续的
*/
private final Class<K> keyType;
/**
* All of the values comprising K. (Cached for performance.)
*
* key的数组:包含Key的所有值(缓存以提高性能)
*/
private transient K[] keyUniverse;
/**
* Array representation of this map. The ith element is the value
* to which universe[i] is currently mapped, or null if it isn't
* mapped to anything, or NULL if it's mapped to null.
*
* value数组:如果没有映射,则为空,映射到任何对象,如果映射到NULL,则为NULL
*/
private transient Object[] vals;
/**
* The number of mappings in this map.
* 键值对个数
*/
private transient int size = 0;
/**
* Distinguished non-null value for representing null values.
*
* 用于表示null值的可分辨非空值:value 为 null 时对应的值【???】。
*/
private static final Object NULL = new Object() {
public int hashCode() {
return 0;
}
public String toString() {
return "java.util.EnumMap.NULL";
}
};
/**
* 此枚举常量的序号(其位置在枚举声明中,初始常量被赋值零的序数)
*/
private final int ordinal;
/**
* 返回此枚举常量的序号(其位置在其enum声明中,初始常量被赋值零的序数)
*/
public final int ordinal() {
return ordinal;
}
2、EnumMap-源码分析-构造方法
1、EnumMap-源码分析-构造方法:EnumMap 共提供了 3 个构造函数
2、源代码:
/**
* 创建具有指定键类型的空枚举映射。
*
* @param keyType此枚举映射的键类型的类对象
* @throws 如果keyType为null,则@throws NullPointerException
*/
public EnumMap(Class<K> keyType) {
this.keyType = keyType;
// 初始化 key 数组,getKeyUniverse 方法会计算出枚举元素的总数并初始化 key 数组
keyUniverse = getKeyUniverse(keyType);
// 初始化 value 数组大小
vals = new Object[keyUniverse.length];
}
/**
* 创建与指定枚举具有相同键类型的枚举映射
*
* @param m m从中初始化此枚举映射的枚举映射
* @throws NullPointerException if <tt>m</tt> is null
*/
public EnumMap(EnumMap<K, ? extends V> m) {
keyType = m.keyType;
keyUniverse = m.keyUniverse;
vals = m.vals.clone();
size = m.size;
}
/**
* 创建从指定映射初始化的枚举映射。如果指定的映射是一个<tt>EnumMap</tt>实例
*
* @param m m从中初始化此枚举映射的映射
* @throws IllegalArgumentException if 如果<tt>m</tt>不是<tt>EnumMap</tt>实例
* @throws NullPointerException if <tt>m</tt> is null
*/
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);
}
}
3、构造方法中涉及的方法
/**
* 返回包含K的所有值,结果将被所有调用方取消锁定、缓存和共享。
*/
private static <K extends Enum<K>> K[] getKeyUniverse(Class<K> keyType) {
//通过 JavaLangAccess 和 SharedSecrets 来获取 JVM 中对象实例
return SharedSecrets.getJavaLangAccess().getEnumConstantsShared(keyType);
}
3、EnumMap-源码分析-添加元素
1、EnumMap-源码分析-添加元素:.put()方法
2、源代码:
public V put(K key, V value) {
// key 类型检查,如果 key 的类型不一致会抛出异常(ClassCastException)。
typeCheck(key);
// 获得该 key 对应的位置
int index = key.ordinal();
// 在 vals 数组中获取 key 角标对应的 value
Object oldValue = vals[index];
// 覆盖或设置 value
vals[index] = maskNull(value);
// 如果 key 对应的位置 value 为 null,则表示新插入了键值对,size++,反之表示值覆盖 size 不变
if (oldValue == null)
size++;
return unmaskNull(oldValue);
}
//添加的 value 为 null 会通过 maskNull 方法特殊处理,存储一个 Object 对象。
private Object maskNull(Object value) {
return (value == null ? NULL : value);
}
//值覆盖的话,put 方法会返回旧的 value 值,并特殊处理 value 为 null 的情况:
private V unmaskNull(Object value) {
return (V)(value == NULL ? null : value);
}
3、添加元素方法-小结
3-1、EnmuMap 添加键值对并没有扩容操作,因为一个枚举类型到底有多少元素在代码运行阶段是确定的,在构造函数中已经对 key 数组进行了初始化与赋值,value 数组的大小也已经被确定。
3-2、还有一个需要注意的问题,在上面的 .put()方法中只对 value 进行了处理,并没有处理 key,原因就是 key 数组在构造函数中已经被赋值了。
4、EnumMap-源码分析-获取元素
1、EnumMap-源码分析-获取元素:.get()方法
2、源代码:
public V get(Object key) {
return (isValidKey(key) ?
unmaskNull(vals[((Enum<?>)key).ordinal()]) : null);
}
//调用 isValidKey 方法对 key 进行一次检查
private boolean isValidKey(Object key) {
// key 为 null 直接返回 false
if (key == null)
return false;
// Cheaper than instanceof Enum followed by getDeclaringClass
Class<?> keyClass = key.getClass();
// key 类型检查
return keyClass == keyType || keyClass.getSuperclass() == keyType;
}
//特殊处理 value 为 null 的情况:
private V unmaskNull(Object value) {
return (V)(value == NULL ? null : value);
}
//EnumMap 存储键值对时并不会根据 key 获取对应的哈希值,enum 本身已经提供了一个 ordinal() 方法,该方法会返回具体枚举元素在枚举类中的位置(从 0 开始),因此一个枚举元素从创建就已经有了一个唯一索引与其对应,这样就不存在哈希冲突的问题了。
5、EnumMap-源码分析-删除元素
1、EnumMap-源码分析-删除元素:.remove()方法
2、源代码:
/**
* Removes the mapping for this key from this map if present.
*
* @param key the key whose mapping is to be removed from the map
* @return the previous value associated with specified key, or
* <tt>null</tt> if there was no entry for key. (A <tt>null</tt>
* return can also indicate that the map previously associated
* <tt>null</tt> with the specified key.)
*/
public V remove(Object key) {
// key 类型错误的时候直接返回 null
if (!isValidKey(key))
return null;
// 根据 key 计算出其在枚举中位置
int index = ((Enum<?>)key).ordinal();
// 获取对应的 value
Object oldValue = vals[index];
// value 置 null,下次 GC 回收
vals[index] = null;
// 如果对应的 value 不为 null,如果添加键值对的时候 value 为 null,则存储的是 NULL(Object)
if (oldValue != null)
size--;
return unmaskNull(oldValue);
}
//调用 isValidKey 方法对 key 进行一次检查
private boolean isValidKey(Object key) {
// key 为 null 直接返回 false
if (key == null)
return false;
// Cheaper than instanceof Enum followed by getDeclaringClass
Class<?> keyClass = key.getClass();
// key 类型检查
return keyClass == keyType || keyClass.getSuperclass() == keyType;
}
6、EnumMap-源码分析-遍历元素
1、EnumMap-遍历元素:与Map遍历方式差不多
2、EnumMap-遍历元素-特殊之处:
//举例Demo
public static void main(String[] args) throws Exception {
EnumMap<Season, String> map = new EnumMap<>(Season.class);
// 获取迭代器对象
Iterator<Map.Entry<Season, String>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
//并不会输出任何 key,原因就在于 EnumMap 的 hasNext() 方法中对 value 做了非空判断
System.out.println(iterator.next().getKey());
}
}
//尽管在构造函数中 key 数组已经被初始化,但是如果对应的 value 为 null,在迭代的时候也会被过滤掉。
//通过.hasNext()方法跳过空的数组
public boolean hasNext() {
// 循环中会略过 value 数组中为 null 的情况
while (index < vals.length && vals[index] == null)
index++;
return index != vals.length;
}
3、EnumMapIterator的迭代
private class EntryIterator extends EnumMapIterator<Map.Entry<K,V>> {
private Entry lastReturnedEntry;
public Map.Entry<K,V> next() {
//通过.hasNext()方法跳过空的数组
if (!hasNext())
throw new NoSuchElementException();
lastReturnedEntry = new Entry(index++);
return lastReturnedEntry;
}
public void remove() {
lastReturnedIndex =
((null == lastReturnedEntry) ? -1 : lastReturnedEntry.index);
super.remove();
lastReturnedEntry.index = lastReturnedIndex;
lastReturnedEntry = null;
}
private class Entry implements Map.Entry<K,V> {
private int index;
private Entry(int index) {
this.index = index;
}
public K getKey() {
checkIndexForEntryUse();
return keyUniverse[index];
}
public V getValue() {
checkIndexForEntryUse();
return unmaskNull(vals[index]);
}
public V setValue(V value) {
checkIndexForEntryUse();
V oldValue = unmaskNull(vals[index]);
vals[index] = maskNull(value);
return oldValue;
}
public boolean equals(Object o) {
if (index < 0)
return o == this;
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
V ourValue = unmaskNull(vals[index]);
Object hisValue = e.getValue();
return (e.getKey() == keyUniverse[index] &&
(ourValue == hisValue ||
(ourValue != null && ourValue.equals(hisValue))));
}
public int hashCode() {
if (index < 0)
return super.hashCode();
return entryHashCode(index);
}
public String toString() {
if (index < 0)
return super.toString();
return keyUniverse[index] + "="
+ unmaskNull(vals[index]);
}
private void checkIndexForEntryUse() {
if (index < 0)
throw new IllegalStateException("Entry was removed");
}
}
}