Effective Java~37. 用EnumMap 代替序数索引
? ? ? ?有時可能會看到使用 ordinal 方法(條目 35)來索引到數組或列表的代碼。 例如,考慮一下這個簡單的類來代表一種植物:
class Plant {enum LifeCycle { ANNUAL, PERENNIAL, BIENNIAL }final String name;final LifeCycle lifeCycle;Plant(String name, LifeCycle lifeCycle) {this.name = name;this.lifeCycle = lifeCycle;}@Override public String toString() {return name;} }????????現在假設你有一組植物代表一個花園,想要列出這些由生命周期組織的植物 (一年生,多年生,或雙年生)。為此,需要構建三個集合,每個生命周期作為一個,并遍歷整個花園,將每個植物放置在適當的集合中。
// Using ordinal() to index into an array - DON'T DO THIS! Set<Plant>[] plantsByLifeCycle = (Set<Plant>[]) new Set[Plant.LifeCycle.values().length]; for (int i = 0; i < plantsByLifeCycle.length; i++)plantsByLifeCycle[i] = new HashSet<>();for (Plant p : garden)plantsByLifeCycle[p.lifeCycle.ordinal()].add(p);// Print the results for (int i = 0; i < plantsByLifeCycle.length; i++) {System.out.printf("%s: %s%n",Plant.LifeCycle.values()[i], plantsByLifeCycle[i]); }????????這種方法是有效的,但充滿了問題。 因為數組不兼容泛型(條目 28),程序需要一個未經檢查的轉換,并且不會干凈地編譯。 由于該數組不知道索引代表什么,因此必須手動標記索引輸出。 但是這種技術最嚴重的問題是,當你訪問一個由枚舉序數索引的數組時,你有責任使用正確的 int 值; int 不提供枚舉的類型安全性。 如果你使用了錯誤的值,程序會默默地做錯誤的事情,如果你幸運的話,拋出一個 ArrayIndexOutOfBoundsException 異常。
????????有一個更好的方法來達到同樣的效果。 該數組有效地用作從枚舉到值的映射,因此不妨使用 Map 。 更具體地說,有一個非常快速的 Map 實現,設計用于枚舉鍵,稱為 java.util.EnumMap 。 下面是當程序重寫為使用EnumMap 時的樣子:
// Using an EnumMap to associate data with an enum Map<Plant.LifeCycle, Set<Plant>> plantsByLifeCycle = new EnumMap<>(Plant.LifeCycle.class);for (Plant.LifeCycle lc : Plant.LifeCycle.values())plantsByLifeCycle.put(lc, new HashSet<>());for (Plant p : garden)plantsByLifeCycle.get(p.lifeCycle).add(p);System.out.println(plantsByLifeCycle);????????這段程序更簡短,更清晰,更安全,運行速度與原始版本相當。 沒有不安全的轉換; 無需手動標記輸出,因為map 鍵是知道如何將自己轉換為可打印字符串的枚舉; 并且不可能在計算數組索引時出錯。 EnumMap 與序數索引數組的速度相當,其原因是 EnumMap 內部使用了這樣一個數組,但它對程序員的隱藏了這個實現細節,將 Map 的豐富性和類型安全性與數組的速度相結合。 請注意, EnumMap 構造方法接受鍵類Class 型的 Class 對象:這是一個有限定的類型令牌(bounded type token),它提供運行時的泛型類型信息(條目 33)。
????????通過使用 stream (條目 45)來管理 Map ,可以進一步縮短以前的程序。 以下是最簡單的基于 stream 的代碼,它們在很大程度上重復了前面示例的行為:
// Naive stream-based approach - unlikely to produce an EnumMap! System.out.println(Arrays.stream(garden).collect(groupingBy(p -> p.lifeCycle)));????????這個代碼的問題在于它選擇了自己的 Map 實現,實際上它不是 EnumMap ,所以它不會與顯式 EnumMap 的版本的空間和時間性能相匹配。 為了解決這個問題,使用 Collectors.groupingBy 的三個參數形式的方法,它允許調用者使用 mapFactory 參數指定 map 的實現:
// Using a stream and an EnumMap to associate data with an enum System.out.println(Arrays.stream(garden).collect(groupingBy(p -> p.lifeCycle,() -> new EnumMap<>(LifeCycle.class), toSet())));????????這樣的優化在像這樣的示例程序中是不值得的,但是在大量使用 Map 的程序中可能是至關重要的。
總結
以上是生活随笔為你收集整理的Effective Java~37. 用EnumMap 代替序数索引的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring Data JPA 从入门到
- 下一篇: JPA - EntityTransact