一、概述
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將是非常高效的。