EnumMap 是 Java 集合框架中專為枚舉鍵設計的 Map 實現。由於枚舉值固定且有限,EnumMap 內部採用數組存儲,完全避免了哈希衝突與擴容開銷,讀寫性能極高。本文從初始化、存儲、取值、鍵存在性判斷四個維度,深度剖析其源碼實現機制。
普通 HashMap 通過哈希值定位桶位,再通過 equals 比較鍵。而枚舉類型天然具備:
values() 返回所有實例)ordinal()(聲明順序索引,從 0 開始)== 比較即可判斷相等因此,EnumMap 直接以 ordinal() 作為數組下標,實現 O(1) 精確定位,無需哈希計算、無需鏈表/紅黑樹解決衝突,內存佔用也極小。
// 方式一:指定枚舉 Class,反射獲取所有枚舉常量
public EnumMap(Class<K> keyType) { ... }
// 方式二:複製另一個 EnumMap
public EnumMap(EnumMap<K, ? extends V> m) { ... }
// 方式三:從任意 Map 複製(需鍵為同一枚舉類型)
public EnumMap(Map<K, ? extends V> m) { ... }
內部維護兩個平行數組:
Object[] keys:存儲所有枚舉常量,長度 = 枚舉值個數,可跨實例共享(同一枚舉類型的 keys 內容完全一致)Object[] values:存儲對應的值,長度與 keys 相同public V put(K key, V value) {
typeCheck(key); // 確保 key 屬於正確枚舉類型
int index = key.ordinal(); // 直接獲取數組下標
Object oldValue = values[index];
values[index] = maskNull(value); // null 值特殊處理
if (oldValue == null) size++;
return unmaskNull(oldValue);
}
關鍵點:
typeCheck(key) 通過 key.getClass() != keyType 確保類型安全maskNull / unmaskNull 機制:用單例對象 NULL 代替真正的 null,區別「值為 null」與「未設置值」兩種狀態public V get(Object key) {
return (isValidKey(key) ? unmaskNull(values[((Enum)key).ordinal()]) : null);
}
直接通過 ordinal() 定位數組,零哈希計算、零 equals 比較,極致高效。
public boolean containsKey(Object key) {
return isValidKey(key) && values[((Enum)key).ordinal()] != null;
}
同理,檢查對應下標的 values 是否為 NULL 單例或真實值。
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);
}
因為 values 數組初始化為 null 表示「無值」,若直接存 null 則無法區分「已存 null」與「未存」。引入 NULL 佔位對象完美解決此問題。
| 特性 | EnumMap | HashMap |
|---|---|---|
| 鍵類型 | 僅枚舉 | 任意對象 |
| 底層結構 | 數組 | 數組+鏈表/紅黑樹 |
| 定位方式 | ordinal() 直接索引 | hashCode() + equals |
| 內存佔用 | 極小(2 個小數組) | 較大(Entry 對象、桶數組等) |
| 擴容機制 | 無(容量固定) | 有(負載因子觸發) |
| 線程安全 | 否 | 否 |
Map<Enum, V> 所有使用場景EnumMap 是「以空間換時間、以約束換性能」的典範:利用枚舉的固有屬性(有限、有序、可索引),將 Map 操作降維為數組訪問。若業務鍵天然為枚舉類型,**毫無理由不使用 EnumMap**——它更快、更輕、更簡潔。