Effective Java Item37:用 EnumMap 取代 int 索引的陣列
整理Effective Java書中Item 37: Use EnumMap instead of ordinal indexing心得筆記
主旨
在 Java 中,我們有時會用 int 索引的陣列來儲存與 enum 相關的資料,但這種做法容易造成型別不安全和可讀性問題。Java 提供了 EnumMap 類別,專門用來處理 enum 類型的映射關係,提供更安全、更清楚的解決方案。
問題:int 索引陣列的缺點
假設我們要儲存每個星期幾的開門時間,傳統作法可能會這樣寫:
public class StoreHours {
private static final int[] OPENING_HOURS = {
9, // Monday
9, // Tuesday
9, // Wednesday
9, // Thursday
9, // Friday
10, // Saturday
10 // Sunday
};
public int getOpeningHour(Day day) {
return OPENING_HOURS[day.ordinal()];
}
}
這種寫法有幾個問題:
- 型別不安全:
OPENING_HOURS可以接受任何整數值 - 可讀性差:必須靠註解來說明每個索引的意義
- 維護成本高:如果 enum 新增或移除元素,陣列大小也要跟著改
解決方案:使用 EnumMap
EnumMap 是專門為 enum 設計的 Map 實作,它比一般 HashMap 更有效率,因為它知道 key 的數量是固定的。
public class StoreHours {
private final EnumMap<Day, Integer> openingHours = new EnumMap<>(Day.class);
public StoreHours() {
openingHours.put(Day.MONDAY, 9);
openingHours.put(Day.TUESDAY, 9);
openingHours.put(Day.WEDNESDAY, 9);
openingHours.put(Day.THURSDAY, 9);
openingHours.put(Day.FRIDAY, 9);
openingHours.put(Day.SATURDAY, 10);
openingHours.put(Day.SUNDAY, 10);
}
public int getOpeningHour(Day day) {
return openingHours.get(day);
}
}
使用 EnumMap 的好處:
- 型別安全:只能接受
Day類型的 key - 可讀性高:直接用 enum 值做 key,不用靠註解
- 維護容易:新增或移除 enum 元素時,EnumMap 會自動處理
- 效能好:因為 key 數量固定,所以比 HashMap 更有效率
實作範例
假設我們要建立一個應用程式設定系統,用於管理系統的各種配置選項:
public enum ConfigKey {
MAX_CONNECTIONS, TIMEOUT, CACHE_SIZE, LOG_LEVEL, MAX_THREADS
}
public class ConfigurationSettings {
private final EnumMap<ConfigKey, Object> settings = new EnumMap<>(ConfigKey.class);
public ConfigurationSettings() {
// 預設設定值
settings.put(ConfigKey.MAX_CONNECTIONS, 100);
settings.put(ConfigKey.TIMEOUT, 5000);
settings.put(ConfigKey.CACHE_SIZE, 1024);
settings.put(ConfigKey.LOG_LEVEL, "INFO");
settings.put(ConfigKey.MAX_THREADS, 20);
}
// 設定值方法
public void setSetting(ConfigKey key, Object value) {
settings.put(key, value);
}
// 取得設定值方法
@SuppressWarnings("unchecked")
public <T> T getSetting(ConfigKey key) {
return (T) settings.get(key);
}
// 驗證設定值的範圍
public boolean validateSettings() {
int maxConnections = getSetting(ConfigKey.MAX_CONNECTIONS);
if (maxConnections < 1 || maxConnections > 1000) return false;
int timeout = getSetting(ConfigKey.TIMEOUT);
if (timeout < 1000 || timeout > 30000) return false;
return true;
}
// 重置設定到預設值
public void resetToDefaults() {
settings.clear();
// 重新設定預設值
settings.put(ConfigKey.MAX_CONNECTIONS, 100);
settings.put(ConfigKey.TIMEOUT, 5000);
settings.put(ConfigKey.CACHE_SIZE, 1024);
settings.put(ConfigKey.LOG_LEVEL, "INFO");
settings.put(ConfigKey.MAX_THREADS, 20);
}
}
// 使用方式
public class SystemManager {
private final ConfigurationSettings config = new ConfigurationSettings();
public void configureSystem() {
// 設定系統參數
config.setSetting(ConfigKey.MAX_CONNECTIONS, 200);
config.setSetting(ConfigKey.TIMEOUT, 10000);
config.setSetting(ConfigKey.CACHE_SIZE, 2048);
config.setSetting(ConfigKey.LOG_LEVEL, "DEBUG");
config.setSetting(ConfigKey.MAX_THREADS, 50);
// 驗證設定
if (!config.validateSettings()) {
throw new IllegalArgumentException("Invalid configuration values");
}
// 取得設定值
int maxThreads = config.getSetting(ConfigKey.MAX_THREADS);
System.out.println("Maximum threads: " + maxThreads);
}
}
小結
EnumMap 提供了比 int 索引陣列更安全、更清楚的解決方案。它特別適合用在需要將 enum 值映射到其他值的情況下,能讓程式碼更易讀、更不易出錯。下次當你需要儲存與 enum 相關的資料時,考慮使用 EnumMap 來取代傳統的 int 索引陣列吧!
Read other posts