首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Java EnumMap 深度解析:用法、原理與 LRU 快取實作

     編輯:Java 綜合教學

Java EnumMap 深度解析:用法、原理與 LRU 快取實作

當 Map 的鍵為列舉型別時,EnumMap 是效能與記憶體的最佳選擇。本文涵蓋基本用法、內部實現原理、與陣列方案比較,並示範如何繼承實作高效能 LRU 快取。

為什麼需要 EnumMap?

列舉型別具備兩大特徵:

  1. 值有限且預先定義(values() 固定)。
  2. 每個值有固定順序(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 Map countBySize(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;
}

測試與輸出

List list = 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 複製(鍵必須為同一列舉型別)。

EnumMap vs 陣列方案

雖可用 int[] stats = new int[Size.values().length] 搭配 ordinal() 手動統計,但 EnumMap 封裝了索引映射、null 值處理、型別安全與標準 Map 介面,更簡潔、安全、方便,內部同樣用陣列實現,效能無損耗。

內部實現原理(基於 JDK 7/8)

核心欄位

private final Class keyType;      // 列舉 Class 物件
private transient K[] keyUniverse;   // 所有列舉值陣列(keys())
private transient Object[] vals;     // 對應值陣列
private transient int size = 0;      // 實際鍵值對數

建構子初始化

public EnumMap(Class keyType) {
    this.keyType = keyType;
    keyUniverse = getKeyUniverse(keyType); // 取得 values() 陣列
    vals = new Object[keyUniverse.length];
}

put 核心邏輯

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 代表「無鍵值對」,但使用者可能想存 null 值。EnumMap 內部定義一個 private static final Object NULL = new Object() {...} 作為哨兵,maskNull()/unmaskNull() 封裝轉換。

繼承 EnumMap 實作 LRU 快取

LinkedHashMap 有 removeEldestEntry 鉤子,EnumMap 同樣提供 protected boolean removeEldestEntry(Entry eldest),預設回傳 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; // 超過容量移除最舊(最久未訪問)
    }
}

使用示範

LRUCache cache = 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 初始化內部陣列。
  • 內部雙陣列 + ordinal 索引,O(1) 存取,零雜湊開銷。
  • 繼承並覆寫 removeEldestEntry 輕鬆實作型別安全的 LRU 快取。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved