<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                ## Chapter 6. Enums and Annotations(枚舉和注解) ### Item 37: Use EnumMap instead of ordinal indexing(使用 EnumMap 替換序數索引) Occasionally you may see code that uses the ordinal method (Item 35) to index into an array or list. For example, consider this simplistic class meant to represent a plant: 偶爾你可能會看到使用 `ordinal()` 的返回值([Item-35](/Chapter-6/Chapter-6-Item-35-Use-instance-fields-instead-of-ordinals.md))作為數組或 list 索引的代碼。例如,考慮這個簡單的類,它表示一種植物: ``` 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; } } ``` Now suppose you have an array of plants representing a garden, and you want to list these plants organized by life cycle (annual, perennial, or biennial). To do this, you construct three sets, one for each life cycle, and iterate through the garden, placing each plant in the appropriate set. Some programmers would do this by putting the sets into an array indexed by the life cycle’s ordinal: 現在假設你有一個代表花園全部植物的 Plant 數組,你想要列出按生命周期(一年生、多年生或兩年生)排列的植物。要做到這一點,你需要構造三個集合,每個生命周期一個,然后遍歷整個數組,將每個植物放入適當的集合中: ``` // 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]); } ``` **譯注:假設 Plant 數組如下:** ``` Plant[] garden = new Plant[]{ new Plant("A", LifeCycle.ANNUAL), new Plant("B", LifeCycle.BIENNIAL), new Plant("C", LifeCycle.PERENNIAL), new Plant("D", LifeCycle.BIENNIAL), new Plant("E", LifeCycle.PERENNIAL), }; ``` 輸出結果為: ``` ANNUAL: [A] PERENNIAL: [E, C] BIENNIAL: [B, D] ``` This technique works, but it is fraught with problems. Because arrays are not compatible with generics (Item 28), the program requires an unchecked cast and will not compile cleanly. Because the array does not know what its index represents, you have to label the output manually. But the most serious problem with this technique is that when you access an array that is indexed by an enum’s ordinal, it is your responsibility to use the correct int value; ints do not provide the type safety of enums. If you use the wrong value, the program will silently do the wrong thing or—if you’re lucky—throw an ArrayIndexOutOfBoundsException. 這種技術是有效的,但它充滿了問題。因為數組與泛型不兼容([Item-28](/Chapter-5/Chapter-5-Item-28-Prefer-lists-to-arrays.md)),所以該程序需要 unchecked 的轉換,否則不能順利地編譯。因為數組不知道它的索引表示什么,所以必須手動標記輸出。但是這種技術最嚴重的問題是,當你訪問一個由枚舉序數索引的數組時,你有責任使用正確的 int 值;int 不提供枚舉的類型安全性。如果你使用了錯誤的值,程序將靜默執行錯誤的操作,如果幸運的話,才會拋出 ArrayIndexOutOfBoundsException。 There is a much better way to achieve the same effect. The array is effectively serving as a map from the enum to a value, so you might as well use a Map. More specifically, there is a very fast Map implementation designed for use with enum keys, known as java.util.EnumMap. Here is how the program looks when it is rewritten to use EnumMap: 有一種更好的方法可以達到同樣的效果。該數組有效地充當從枚舉到值的映射,因此你不妨使用 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); ``` This program is shorter, clearer, safer, and comparable in speed to the original version. There is no unsafe cast; no need to label the output manually because the map keys are enums that know how to translate themselves to printable strings; and no possibility for error in computing array indices. The reason that EnumMap is comparable in speed to an ordinal-indexed array is that EnumMap uses such an array internally, but it hides this implementation detail from the programmer, combining the richness and type safety of a Map with the speed of an array. Note that the EnumMap constructor takes the Class object of the key type: this is a bounded type token, which provides runtime generic type information (Item 33). 這個程序比原來的版本更短,更清晰,更安全,速度也差不多。沒有不安全的轉換;不需要手動標記輸出,因為 Map 的鍵是能轉換為可打印字符串的枚舉;在計算數組索引時不可能出錯。EnumMap 在速度上與有序索引數組相當的原因是,EnumMap 在內部使用這樣的數組,但是它向程序員隱藏了實現細節,將 Map 的豐富的功能和類型安全性與數組的速度結合起來。注意,EnumMap 構造函數接受鍵類型的 Class 對象:這是一個有界類型標記,它提供運行時泛型類型信息([Item-33](/Chapter-5/Chapter-5-Item-33-Consider-typesafe-heterogeneous-containers.md))。 The previous program can be further shortened by using a stream (Item 45) to manage the map. Here is the simplest stream-based code that largely duplicates the behavior of the previous example: 通過使用流([Item-45](/Chapter-7/Chapter-7-Item-45-Use-streams-judiciously.md))來管理映射,可以進一步縮短前面的程序。下面是基于流的最簡單的代碼,它在很大程度上復制了前一個示例的行為: ``` // Naive stream-based approach - unlikely to produce an EnumMap! System.out.println(Arrays.stream(garden).collect(groupingBy(p -> p.lifeCycle))); ``` **譯注:以上代碼需要引入 `java.util.stream.Collectors.groupingBy`,輸出結果如下:** ``` {BIENNIAL=[B, D], ANNUAL=[A], PERENNIAL=[C, E]} ``` The problem with this code is that it chooses its own map implementation, and in practice it won’t be an EnumMap, so it won’t match the space and time performance of the version with the explicit EnumMap. To rectify this problem, use the three-parameter form of Collectors.groupingBy, which allows the caller to specify the map implementation using the mapFactory parameter: 這段代碼的問題在于它選擇了自己的 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())) ); ``` **譯注:以上代碼需要引入 `java.util.stream.Collectors.toSet`** This optimization would not be worth doing in a toy program like this one but could be critical in a program that made heavy use of the map. 這種優化在示例程序中不值得去做,但在大量使用 Map 的程序中可能非常重要。 The behavior of the stream-based versions differs slightly from that of the EmumMap version. The EnumMap version always makes a nested map for each plant lifecycle, while the stream-based versions only make a nested map if the garden contains one or more plants with that lifecycle. So, for example, if the garden contains annuals and perennials but no biennials, the size of plantsByLifeCycle will be three in the EnumMap version and two in both of the stream-based versions. 基于流的版本的行為與 EmumMap 版本略有不同。EnumMap 版本總是為每個植物生命周期生成一個嵌套 Map,而基于流的版本只在花園包含具有該生命周期的一個或多個植物時才生成嵌套 Map。例如,如果花園包含一年生和多年生植物,但沒有兩年生植物,plantsByLifeCycle 的大小在 EnumMap 版本中為 3,在基于流的版本中為 2。 You may see an array of arrays indexed (twice!) by ordinals used to represent a mapping from two enum values. For example, this program uses such an array to map two phases to a phase transition (liquid to solid is freezing, liquid to gas is boiling, and so forth): 你可能會看到被序數索引(兩次!)的數組,序數用于表示兩個枚舉值的映射。例如,這個程序使用這樣的一個數組來映射兩個狀態到一個狀態的轉換過程(液體到固體是凍結的,液體到氣體是沸騰的,等等): ``` // Using ordinal() to index array of arrays - DON'T DO THIS! public enum Phase { SOLID, LIQUID, GAS; public enum Transition { MELT, FREEZE, BOIL, CONDENSE, SUBLIME, DEPOSIT; // Rows indexed by from-ordinal, cols by to-ordinal private static final Transition[][] TRANSITIONS = { { null, MELT, SUBLIME }, { FREEZE, null, BOIL }, { DEPOSIT, CONDENSE, null } }; // Returns the phase transition from one phase to another public static Transition from(Phase from, Phase to) { return TRANSITIONS[from.ordinal()][to.ordinal()]; } } } ``` **譯注:固體、液體、氣體三態,對應的三組變化:融化 MELT,凍結 FREEZE(固態與液態);沸騰 BOIL,凝固 CONDENSE(液態與氣態);升華 SUBLIME,凝華 DEPOSIT(固態與氣態)。** This program works and may even appear elegant, but appearances can be deceiving. Like the simpler garden example shown earlier, the compiler has no way of knowing the relationship between ordinals and array indices. If you make a mistake in the transition table or forget to update it when you modify the Phase or Phase.Transition enum type, your program will fail at runtime. The failure may be an ArrayIndexOutOfBoundsException, a NullPointerException, or (worse) silent erroneous behavior. And the size of the table is quadratic in the number of phases, even if the number of non-null entries is smaller. 這個程序可以工作,甚至可能看起來很優雅,但外表可能具有欺騙性。就像前面展示的更簡單的 garden 示例一樣,編譯器無法知道序數和數組索引之間的關系。如果你在轉換表中出錯,或者在修改 Phase 或 `Phase.Transition` 枚舉類型時忘記更新,你的程序將在運行時失敗。失敗可能是拋出 ArrayIndexOutOfBoundsException、NullPointerException 或(更糟糕的)靜默錯誤行為。并且即使非空項的數目更小,該表的大小也為狀態數量的二次方。 Again, you can do much better with EnumMap. Because each phase transition is indexed by a pair of phase enums, you are best off representing the relationship as a map from one enum (the “from” phase) to a map from the second enum (the “to” phase) to the result (the phase transition). The two phases associated with a phase transition are best captured by associating them with the phase transition enum, which can then be used to initialize the nested EnumMap: 同樣,使用 EnumMap 可以做得更好。因為每個階段轉換都由一對階段枚舉索引,所以最好將這個關系用 Map 表示,從一個枚舉(起始階段)到第二個枚舉(結束階段)到結果(轉換階段)。與階段轉換相關聯的兩個階段最容易捕捉到的是將它們與階段過渡的 enum 聯系起來,這樣就可以用來初始化嵌套的 EnumMap: ``` // Using a nested EnumMap to associate data with enum pairs public enum Phase { SOLID, LIQUID, GAS; public enum Transition { MELT(SOLID, LIQUID), FREEZE(LIQUID, SOLID), BOIL(LIQUID, GAS), CONDENSE(GAS, LIQUID), SUBLIME(SOLID, GAS), DEPOSIT(GAS, SOLID); private final Phase from; private final Phase to; Transition(Phase from, Phase to) { this.from = from; this.to = to; } // Initialize the phase transition map private static final Map<Phase_new, Map<Phase_new, Transition>> m = Stream.of(values()) .collect(groupingBy( t -> t.from, () -> new EnumMap<>(Phase_new.class), toMap(t -> t.to, t -> t, (x, y) -> y, () -> new EnumMap<>(Phase_new.class)) ) ); public static Transition from(Phase from, Phase to) { return m.get(from).get(to); } } } ``` The code to initialize the phase transition map is a bit complicated. The type of the map is `Map<Phase, Map<Phase, Transition>>`, which means “map from (source) phase to map from (destination) phase to transition.” This map-of-maps is initialized using a cascaded sequence of two collectors. The first collector groups the transitions by source phase, and the second creates an EnumMap with mappings from destination phase to transition. The merge function in the second collector ((x, y) -> y)) is unused; it is required only because we need to specify a map factory in order to get an EnumMap, and Collectors provides telescoping factories. The previous edition of this book used explicit iteration to initialize the phase transition map. The code was more verbose but arguably easier to understand. 初始化階段變化 Map 的代碼有點復雜。Map 的類型是 `Map<Phase, Map<Phase, Transition>>`,這意味著「從(源)階段 Map 到(目標)階段 Map 的轉換過程」。這個 Map 嵌套是使用兩個收集器的級聯序列初始化的。第一個收集器按源階段對轉換進行分組,第二個收集器使用從目標階段到轉換的映射創建一個 EnumMap。第二個收集器 ((x, y) -> y) 中的 merge 函數未使用;之所以需要它,只是因為我們需要指定一個 Map 工廠來獲得 EnumMap,而 Collector 提供了伸縮工廠。本書的上一個版本使用顯式迭代來初始化階段轉換映射。代碼更冗長,但也更容易理解。 **譯注:第二版中的實現代碼如下:** ``` // Initialize the phase transition map private static final Map<Phase, Map<Phase,Transition> m = new EnumMap<Phase, Map<Phase ,Transition>>(Phase.class); static{ for (Phase p : Phase. values()) m.put(p,new EnumMap<Phase,Transition (Phase.class)); for (Transition trans : Transition.values() ) m.get(trans. src).put(trans.dst, trans) ; } public static Transition from(Phase src, Phase dst) { return m.get(src).get(dst); } ``` Now suppose you want to add a new phase to the system: plasma, or ionized gas. There are only two transitions associated with this phase: ionization, which takes a gas to a plasma; and deionization, which takes a plasma to a gas. To update the array-based program, you would have to add one new constant to Phase and two to Phase.Transition, and replace the original nine-element array of arrays with a new sixteen-element version. If you add too many or too few elements to the array or place an element out of order, you are out of luck: the program will compile, but it will fail at runtime. To update the EnumMap-based version, all you have to do is add PLASMA to the list of phases, and IONIZE(GAS, PLASMA) and DEIONIZE(PLASMA, GAS) to the list of phase transitions: 現在假設你想向系統中加入一種新階段:等離子體,或電離氣體。這個階段只有兩個變化:電離,它把氣體轉為等離子體;去離子作用,把等離子體變成氣體。假設要更新基于數組版本的程序,必須向 Phase 添加一個新常量,向 `Phase.Transition` 添加兩個新常量,并用一個新的 16 個元素版本替換原來的數組中的 9 個元素數組。如果你向數組中添加了太多或太少的元素,或者打亂了元素的順序,那么你就麻煩了:程序將編譯,但在運行時將失敗。相比之下,要更新基于 EnumMap 的版本,只需將 PLASMA 添加到 Phase 列表中,將 `IONIZE(GAS, PLASMA)` 和 `DEIONIZE(PLASMA, GAS)` 添加到 `Phase.Transition` 中: ``` // Adding a new phase using the nested EnumMap implementation public enum Phase { SOLID, LIQUID, GAS, PLASMA; public enum Transition { MELT(SOLID, LIQUID), FREEZE(LIQUID, SOLID), BOIL(LIQUID, GAS), CONDENSE(GAS, LIQUID), SUBLIME(SOLID, GAS), DEPOSIT(GAS, SOLID), IONIZE(GAS, PLASMA), DEIONIZE(PLASMA, GAS); ... // Remainder unchanged } } ``` The program takes care of everything else and leaves you virtually no opportunity for error. Internally, the map of maps is implemented with an array of arrays, so you pay little in space or time cost for the added clarity, safety, and ease of maintenance. 這個程序會處理所有其他事情,實際上不會給你留下任何出錯的機會。在內部,Map 的映射是用一個數組來實現的,因此你只需花費很少的空間或時間成本就可以獲得更好的清晰度、安全性并易于維護。 In the interest of brevity, the above examples use null to indicate the absence of a state change (wherein to and from are identical). This is not good practice and is likely to result in a NullPointerException at runtime. Designing a clean, elegant solution to this problem is surprisingly tricky, and the resulting programs are sufficiently long that they would detract from the primary material in this item. 為了簡潔起見,最初的示例使用 null 表示沒有狀態更改(其中 to 和 from 是相同的)。這不是一個好的方式,可能會在運行時導致 NullPointerException。針對這個問題設計一個干凈、優雅的解決方案是非常棘手的,并且生成的程序冗長,以至于它們會偏離條目中的主要內容。 In summary, **it is rarely appropriate to use ordinals to index into arrays: use EnumMap instead.** If the relationship you are representing is multidimensional, use `EnumMap<..., EnumMap<...>>`. This is a special case of the general principle that application programmers should rarely, if ever, use Enum.ordinal (Item 35). 總之,**用普通的序數索引數組是非常不合適的:應使用 EnumMap 代替。** 如果所表示的關系是多維的,則使用 `EnumMap<..., EnumMap<...>>`。這是一種特殊的基本原則,程序員很少(即使有的話)使用 `Enum.ordinal` ([Item-35](/Chapter-6/Chapter-6-Item-35-Use-instance-fields-instead-of-ordinals.md))。 --- **[Back to contents of the chapter(返回章節目錄)](/Chapter-6/Chapter-6-Introduction.md)** - **Previous Item(上一條目):[Item 36: Use EnumSet instead of bit fields(用 EnumSet 替代位字段)](/Chapter-6/Chapter-6-Item-36-Use-EnumSet-instead-of-bit-fields.md)** - **Next Item(下一條目):[Item 38: Emulate extensible enums with interfaces(使用接口模擬可擴展枚舉)](/Chapter-6/Chapter-6-Item-38-Emulate-extensible-enums-with-interfaces.md)**
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看