當 Map 的鍵為列舉型別時,EnumMap 是效能與記憶體的最佳選擇。本文涵蓋基本用法、內部實現原理、與陣列方案比較,並示範如何繼承實作高效能 LRU 快取。
列舉型別具備兩大特徵:
values() 固定)。ordinal() 返回 0~n-1)。這使得 EnumMap 可用陣列直接索引,省去雜湊計算與碰撞處理,比 HashMap 更快、更省記憶體、且保證迭代順序(依列舉宣告順序)。
public enum Size { SMALL, MEDIUM, LARGE }
class Clothes {
String id; Size size;
public Clothes(String id, Size size) { this.id = id; this.size = size; }
public Size getSize() { return size; }
}
public static MapcountBySize(List clothes) { Map map = new EnumMap<>(Size.class); // 必須傳 Class 物件 for (Clothes c : clothes) { Size size = c.getSize(); map.merge(size, 1, Integer::sum); // Java 8+ 簡潔寫法 } return map; }
Listlist = Arrays.asList( new Clothes("C001", Size.SMALL), new Clothes("C002", Size.LARGE), new Clothes("C003", Size.LARGE), new Clothes("C004", Size.MEDIUM), new Clothes("C005", Size.SMALL), new Clothes("C006", Size.SMALL) ); System.out.println(countBySize(list)); // 輸出:{SMALL=3, MEDIUM=1, LARGE=2} // 順序固定為 SMALL, MEDIUM, LARGE
EnumMap(EnumMap m) :複製另一個 EnumMap。EnumMap(Map m) :從一般 Map 複製(鍵必須為同一列舉型別)。雖可用 int[] stats = new int[Size.values().length] 搭配 ordinal() 手動統計,但 EnumMap 封裝了索引映射、null 值處理、型別安全與標準 Map 介面,更簡潔、安全、方便,內部同樣用陣列實現,效能無損耗。
private final ClasskeyType; // 列舉 Class 物件 private transient K[] keyUniverse; // 所有列舉值陣列(keys()) private transient Object[] vals; // 對應值陣列 private transient int size = 0; // 實際鍵值對數
public EnumMap(ClasskeyType) { this.keyType = keyType; keyUniverse = getKeyUniverse(keyType); // 取得 values() 陣列 vals = new Object[keyUniverse.length]; }
public V put(K key, V value) {
typeCheck(key); // 型別檢查
int index = key.ordinal(); // 直接用 ordinal 當索引
Object oldValue = vals[index];
vals[index] = maskNull(value); // null 包裝為特殊物件
if (oldValue == null) size++;
return unmaskNull(oldValue);
}
陣列預設值為 null 代表「無鍵值對」,但使用者可能想存 null 值。EnumMap 內部定義一個 private static final Object NULL = new Object() {...} 作為哨兵,maskNull()/unmaskNull() 封裝轉換。
LinkedHashMap 有 removeEldestEntry 鉤子,EnumMap 同樣提供 protected boolean removeEldestEntry(Entry,預設回傳 false。覆寫即可限制容量。
public class LRUCache, V> extends EnumMap { private final int maxEntries; public LRUCache(Class keyType, int maxEntries) { super(keyType); this.maxEntries = maxEntries; } @Override protected boolean removeEldestEntry(Entry eldest) { return size() > maxEntries; // 超過容量移除最舊(最久未訪問) } }
LRUCachecache = new LRUCache<>(Size.class, 3); cache.put(Size.SMALL, "S"); cache.put(Size.MEDIUM, "M"); cache.put(Size.LARGE, "L"); cache.get(Size.SMALL); // 訪問 SMALL cache.put(Size.valueOf("XL"), "XL"); // 新增導致 MEDIUM 被淘汰 System.out.println(cache); // {SMALL=S, LARGE=L, XL=XL}
EnumMap。EnumClass.class 初始化內部陣列。removeEldestEntry 輕鬆實作型別安全的 LRU 快取。