1、EnumMap-源码分析-底层数据结构

  1. 1EnumMap-源码分析-底层数据结构:
  2. 1-1、底层使用数组实现(KV 双数组),其key必须为Enum类型,两个数组,一个数组keyUniverse存储key,另一个数组vals存储val,两个数组通过下标对应起来。
  3. 1-2key 数组会在构造函数中根据 keyType 进行初始化,当 EnmumMapvalue null 时会特殊处理为一个 Object 对象。
  4. 2EnumMap-主要属性有:
  5. /**
  6. * The <tt>Class</tt> object for the enum type of all the keys of this map.
  7. *
  8. * 所有kye的枚举类型的<tt>类</tt>对象
  9. * @serial:连续的
  10. */
  11. private final Class<K> keyType;
  12. /**
  13. * All of the values comprising K. (Cached for performance.)
  14. *
  15. * key的数组:包含Key的所有值(缓存以提高性能)
  16. */
  17. private transient K[] keyUniverse;
  18. /**
  19. * Array representation of this map. The ith element is the value
  20. * to which universe[i] is currently mapped, or null if it isn't
  21. * mapped to anything, or NULL if it's mapped to null.
  22. *
  23. * value数组:如果没有映射,则为空,映射到任何对象,如果映射到NULL,则为NULL
  24. */
  25. private transient Object[] vals;
  26. /**
  27. * The number of mappings in this map.
  28. * 键值对个数
  29. */
  30. private transient int size = 0;
  31. /**
  32. * Distinguished non-null value for representing null values.
  33. *
  34. * 用于表示null值的可分辨非空值:value 为 null 时对应的值【???】。
  35. */
  36. private static final Object NULL = new Object() {
  37. public int hashCode() {
  38. return 0;
  39. }
  40. public String toString() {
  41. return "java.util.EnumMap.NULL";
  42. }
  43. };
  44. /**
  45. * 此枚举常量的序号(其位置在枚举声明中,初始常量被赋值零的序数)
  46. */
  47. private final int ordinal;
  48. /**
  49. * 返回此枚举常量的序号(其位置在其enum声明中,初始常量被赋值零的序数)
  50. */
  51. public final int ordinal() {
  52. return ordinal;
  53. }

2、EnumMap-源码分析-构造方法

  1. 1EnumMap-源码分析-构造方法:EnumMap 共提供了 3 个构造函数
  2. 2、源代码:
  3. /**
  4. * 创建具有指定键类型的空枚举映射。
  5. *
  6. * @param keyType此枚举映射的键类型的类对象
  7. * @throws 如果keyType为null,则@throws NullPointerException
  8. */
  9. public EnumMap(Class<K> keyType) {
  10. this.keyType = keyType;
  11. // 初始化 key 数组,getKeyUniverse 方法会计算出枚举元素的总数并初始化 key 数组
  12. keyUniverse = getKeyUniverse(keyType);
  13. // 初始化 value 数组大小
  14. vals = new Object[keyUniverse.length];
  15. }
  16. /**
  17. * 创建与指定枚举具有相同键类型的枚举映射
  18. *
  19. * @param m m从中初始化此枚举映射的枚举映射
  20. * @throws NullPointerException if <tt>m</tt> is null
  21. */
  22. public EnumMap(EnumMap<K, ? extends V> m) {
  23. keyType = m.keyType;
  24. keyUniverse = m.keyUniverse;
  25. vals = m.vals.clone();
  26. size = m.size;
  27. }
  28. /**
  29. * 创建从指定映射初始化的枚举映射。如果指定的映射是一个<tt>EnumMap</tt>实例
  30. *
  31. * @param m m从中初始化此枚举映射的映射
  32. * @throws IllegalArgumentException if 如果<tt>m</tt>不是<tt>EnumMap</tt>实例
  33. * @throws NullPointerException if <tt>m</tt> is null
  34. */
  35. public EnumMap(Map<K, ? extends V> m) {
  36. if (m instanceof EnumMap) {
  37. EnumMap<K, ? extends V> em = (EnumMap<K, ? extends V>) m;
  38. keyType = em.keyType;
  39. keyUniverse = em.keyUniverse;
  40. vals = em.vals.clone();
  41. size = em.size;
  42. } else {
  43. if (m.isEmpty())
  44. throw new IllegalArgumentException("Specified map is empty");
  45. keyType = m.keySet().iterator().next().getDeclaringClass();
  46. keyUniverse = getKeyUniverse(keyType);
  47. vals = new Object[keyUniverse.length];
  48. putAll(m);
  49. }
  50. }
  51. 3、构造方法中涉及的方法
  52. /**
  53. * 返回包含K的所有值,结果将被所有调用方取消锁定、缓存和共享。
  54. */
  55. private static <K extends Enum<K>> K[] getKeyUniverse(Class<K> keyType) {
  56. //通过 JavaLangAccess 和 SharedSecrets 来获取 JVM 中对象实例
  57. return SharedSecrets.getJavaLangAccess().getEnumConstantsShared(keyType);
  58. }

3、EnumMap-源码分析-添加元素

  1. 1EnumMap-源码分析-添加元素:.put()方法
  2. 2、源代码:
  3. public V put(K key, V value) {
  4. // key 类型检查,如果 key 的类型不一致会抛出异常(ClassCastException)。
  5. typeCheck(key);
  6. // 获得该 key 对应的位置
  7. int index = key.ordinal();
  8. // 在 vals 数组中获取 key 角标对应的 value
  9. Object oldValue = vals[index];
  10. // 覆盖或设置 value
  11. vals[index] = maskNull(value);
  12. // 如果 key 对应的位置 value 为 null,则表示新插入了键值对,size++,反之表示值覆盖 size 不变
  13. if (oldValue == null)
  14. size++;
  15. return unmaskNull(oldValue);
  16. }
  17. //添加的 value 为 null 会通过 maskNull 方法特殊处理,存储一个 Object 对象。
  18. private Object maskNull(Object value) {
  19. return (value == null ? NULL : value);
  20. }
  21. //值覆盖的话,put 方法会返回旧的 value 值,并特殊处理 value 为 null 的情况:
  22. private V unmaskNull(Object value) {
  23. return (V)(value == NULL ? null : value);
  24. }
  25. 3、添加元素方法-小结
  26. 3-1EnmuMap 添加键值对并没有扩容操作,因为一个枚举类型到底有多少元素在代码运行阶段是确定的,在构造函数中已经对 key 数组进行了初始化与赋值,value 数组的大小也已经被确定。
  27. 3-2、还有一个需要注意的问题,在上面的 .put()方法中只对 value 进行了处理,并没有处理 key,原因就是 key 数组在构造函数中已经被赋值了。

4、EnumMap-源码分析-获取元素

  1. 1EnumMap-源码分析-获取元素:.get()方法
  2. 2、源代码:
  3. public V get(Object key) {
  4. return (isValidKey(key) ?
  5. unmaskNull(vals[((Enum<?>)key).ordinal()]) : null);
  6. }
  7. //调用 isValidKey 方法对 key 进行一次检查
  8. private boolean isValidKey(Object key) {
  9. // key 为 null 直接返回 false
  10. if (key == null)
  11. return false;
  12. // Cheaper than instanceof Enum followed by getDeclaringClass
  13. Class<?> keyClass = key.getClass();
  14. // key 类型检查
  15. return keyClass == keyType || keyClass.getSuperclass() == keyType;
  16. }
  17. //特殊处理 value 为 null 的情况:
  18. private V unmaskNull(Object value) {
  19. return (V)(value == NULL ? null : value);
  20. }
  21. //EnumMap 存储键值对时并不会根据 key 获取对应的哈希值,enum 本身已经提供了一个 ordinal() 方法,该方法会返回具体枚举元素在枚举类中的位置(从 0 开始),因此一个枚举元素从创建就已经有了一个唯一索引与其对应,这样就不存在哈希冲突的问题了。

5、EnumMap-源码分析-删除元素

  1. 1EnumMap-源码分析-删除元素:.remove()方法
  2. 2、源代码:
  3. /**
  4. * Removes the mapping for this key from this map if present.
  5. *
  6. * @param key the key whose mapping is to be removed from the map
  7. * @return the previous value associated with specified key, or
  8. * <tt>null</tt> if there was no entry for key. (A <tt>null</tt>
  9. * return can also indicate that the map previously associated
  10. * <tt>null</tt> with the specified key.)
  11. */
  12. public V remove(Object key) {
  13. // key 类型错误的时候直接返回 null
  14. if (!isValidKey(key))
  15. return null;
  16. // 根据 key 计算出其在枚举中位置
  17. int index = ((Enum<?>)key).ordinal();
  18. // 获取对应的 value
  19. Object oldValue = vals[index];
  20. // value 置 null,下次 GC 回收
  21. vals[index] = null;
  22. // 如果对应的 value 不为 null,如果添加键值对的时候 value 为 null,则存储的是 NULL(Object)
  23. if (oldValue != null)
  24. size--;
  25. return unmaskNull(oldValue);
  26. }
  27. //调用 isValidKey 方法对 key 进行一次检查
  28. private boolean isValidKey(Object key) {
  29. // key 为 null 直接返回 false
  30. if (key == null)
  31. return false;
  32. // Cheaper than instanceof Enum followed by getDeclaringClass
  33. Class<?> keyClass = key.getClass();
  34. // key 类型检查
  35. return keyClass == keyType || keyClass.getSuperclass() == keyType;
  36. }

6、EnumMap-源码分析-遍历元素

  1. 1EnumMap-遍历元素:与Map遍历方式差不多
  2. 2EnumMap-遍历元素-特殊之处:
  3. //举例Demo
  4. public static void main(String[] args) throws Exception {
  5. EnumMap<Season, String> map = new EnumMap<>(Season.class);
  6. // 获取迭代器对象
  7. Iterator<Map.Entry<Season, String>> iterator = map.entrySet().iterator();
  8. while (iterator.hasNext()) {
  9. //并不会输出任何 key,原因就在于 EnumMap 的 hasNext() 方法中对 value 做了非空判断
  10. System.out.println(iterator.next().getKey());
  11. }
  12. }
  13. //尽管在构造函数中 key 数组已经被初始化,但是如果对应的 value 为 null,在迭代的时候也会被过滤掉。
  14. //通过.hasNext()方法跳过空的数组
  15. public boolean hasNext() {
  16. // 循环中会略过 value 数组中为 null 的情况
  17. while (index < vals.length && vals[index] == null)
  18. index++;
  19. return index != vals.length;
  20. }
  21. 3EnumMapIterator的迭代
  22. private class EntryIterator extends EnumMapIterator<Map.Entry<K,V>> {
  23. private Entry lastReturnedEntry;
  24. public Map.Entry<K,V> next() {
  25. //通过.hasNext()方法跳过空的数组
  26. if (!hasNext())
  27. throw new NoSuchElementException();
  28. lastReturnedEntry = new Entry(index++);
  29. return lastReturnedEntry;
  30. }
  31. public void remove() {
  32. lastReturnedIndex =
  33. ((null == lastReturnedEntry) ? -1 : lastReturnedEntry.index);
  34. super.remove();
  35. lastReturnedEntry.index = lastReturnedIndex;
  36. lastReturnedEntry = null;
  37. }
  38. private class Entry implements Map.Entry<K,V> {
  39. private int index;
  40. private Entry(int index) {
  41. this.index = index;
  42. }
  43. public K getKey() {
  44. checkIndexForEntryUse();
  45. return keyUniverse[index];
  46. }
  47. public V getValue() {
  48. checkIndexForEntryUse();
  49. return unmaskNull(vals[index]);
  50. }
  51. public V setValue(V value) {
  52. checkIndexForEntryUse();
  53. V oldValue = unmaskNull(vals[index]);
  54. vals[index] = maskNull(value);
  55. return oldValue;
  56. }
  57. public boolean equals(Object o) {
  58. if (index < 0)
  59. return o == this;
  60. if (!(o instanceof Map.Entry))
  61. return false;
  62. Map.Entry<?,?> e = (Map.Entry<?,?>)o;
  63. V ourValue = unmaskNull(vals[index]);
  64. Object hisValue = e.getValue();
  65. return (e.getKey() == keyUniverse[index] &&
  66. (ourValue == hisValue ||
  67. (ourValue != null && ourValue.equals(hisValue))));
  68. }
  69. public int hashCode() {
  70. if (index < 0)
  71. return super.hashCode();
  72. return entryHashCode(index);
  73. }
  74. public String toString() {
  75. if (index < 0)
  76. return super.toString();
  77. return keyUniverse[index] + "="
  78. + unmaskNull(vals[index]);
  79. }
  80. private void checkIndexForEntryUse() {
  81. if (index < 0)
  82. throw new IllegalStateException("Entry was removed");
  83. }
  84. }
  85. }