이펙티브 자바 아이템 37. ordinal indexing 대신 EnumMap을 사용하라. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 package com.github.sejoung.codetest.enumtest;import java.util.*;import static java.util.stream.Collectors.groupingBy;import static java.util.stream.Collectors.toSet;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; } public static void main (String[] args) { Plant[] garden = { new Plant ("바질" , LifeCycle.ANNUAL), new Plant ("캐러웨이" , LifeCycle.BIENNIAL), new Plant ("딜" , LifeCycle.ANNUAL), new Plant ("라벤더" , LifeCycle.PERENNIAL), new Plant ("파슬리" , LifeCycle.BIENNIAL), new Plant ("로즈마리" , LifeCycle.PERENNIAL) }; Set<Plant>[] plantsByLifeCycleArr = (Set<Plant>[]) new Set [Plant.LifeCycle.values().length]; for (int i = 0 ; i < plantsByLifeCycleArr.length; i++) plantsByLifeCycleArr[i] = new HashSet <>(); for (Plant p : garden) plantsByLifeCycleArr[p.lifeCycle.ordinal()].add(p); for (int i = 0 ; i < plantsByLifeCycleArr.length; i++) { System.out.printf("%s: %s%n" , Plant.LifeCycle.values()[i], plantsByLifeCycleArr[i]); } } }
실행결과
1 2 3 4 5 6 ANNUAL: [딜, 바질] PERENNIAL: [라벤더, 로즈마리] BIENNIAL: [캐러웨이, 파슬리] Process finished with exit code 0
위에 코드에는 문제점이 많다 배열을 사용해서 제네릭을 사용하지도 못했고 그래서 문제가 될 소지들이 있고 앞에서 말했던 잘못된 동작을 할수있는 사항이 많다
이런 상황에서는 EnumMap이 존재한다 EnumMap으로 대체한 코드는 아래와 같다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 package com.github.sejoung.codetest.enumtest;import java.util.*;import static java.util.stream.Collectors.groupingBy;import static java.util.stream.Collectors.toSet;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; } public static void main (String[] args) { Plant[] garden = { new Plant ("바질" , LifeCycle.ANNUAL), new Plant ("캐러웨이" , LifeCycle.BIENNIAL), new Plant ("딜" , LifeCycle.ANNUAL), new Plant ("라벤더" , LifeCycle.PERENNIAL), new Plant ("파슬리" , LifeCycle.BIENNIAL), new Plant ("로즈마리" , LifeCycle.PERENNIAL) }; 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); } }
실행결과
1 2 3 4 5 6 {ANNUAL=[딜, 바질], PERENNIAL=[라벤더, 로즈마리], BIENNIAL=[캐러웨이, 파슬리]} Process finished with exit code 0
스트림을 사용하면 코드를 줄일수가 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 package com.github.sejoung.codetest.enumtest;import java.util.*;import static java.util.stream.Collectors.groupingBy;import static java.util.stream.Collectors.toSet;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; } public static void main (String[] args) { Plant[] garden = { new Plant ("바질" , LifeCycle.ANNUAL), new Plant ("캐러웨이" , LifeCycle.BIENNIAL), new Plant ("딜" , LifeCycle.ANNUAL), new Plant ("라벤더" , LifeCycle.PERENNIAL), new Plant ("파슬리" , LifeCycle.BIENNIAL), new Plant ("로즈마리" , LifeCycle.PERENNIAL) }; System.out.println(Arrays.stream(garden) .collect(groupingBy(p -> p.lifeCycle))); System.out.println(Arrays.stream(garden) .collect(groupingBy(p -> p.lifeCycle, () -> new EnumMap <>(LifeCycle.class), toSet()))); } }
실행결과
1 2 3 4 5 6 {ANNUAL=[바질, 딜], BIENNIAL=[캐러웨이, 파슬리], PERENNIAL=[라벤더, 로즈마리]} {ANNUAL=[딜, 바질], PERENNIAL=[라벤더, 로즈마리], BIENNIAL=[캐러웨이, 파슬리]} Process finished with exit code 0
중첩을 사용한 케이스(ordinal를 사용)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 package com.github.sejoung.codetest.enumtest;public enum Phase { SOLID, LIQUID, GAS; public enum Transition { MELT, FREEZE, BOIL, CONDENSE, SUBLIME, DEPOSIT; private static final Transition[][] TRANSITIONS = { {null ,MELT,SUBLIME}, {FREEZE,null ,BOIL}, {DEPOSIT,CONDENSE,null } }; public static Transition from (Phase from, Phase to) { return TRANSITIONS[from.ordinal()][to.ordinal()]; } } public static void main (String[] args) { for (Phase src : Phase.values()) { for (Phase dst : Phase.values()) { Transition transition = Transition.from(src, dst); if (transition != null ) System.out.printf("%s에서 %s로 : %s %n" , src, dst, transition); } } } }
실행결과
1 2 3 4 5 6 7 8 9 SOLID에서 LIQUID로 : MELT SOLID에서 GAS로 : SUBLIME LIQUID에서 SOLID로 : FREEZE LIQUID에서 GAS로 : BOIL GAS에서 SOLID로 : DEPOSIT GAS에서 LIQUID로 : CONDENSE Process finished with exit code 0
위에 문제점은 Transition에 추가를 할때 TRANSITIONS 함께 수정하지 않으면 상관관계를 알수가 없다.
그럼 EnumMap으로 수정하면
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 package com.github.sejoung.codetest.enumtest;import java.util.EnumMap;import java.util.Map;import java.util.stream.Stream;import static java.util.stream.Collectors.groupingBy;import static java.util.stream.Collectors.toMap;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; } private static final Map<Phase, Map<Phase, Transition>> m = Stream.of(values()).collect(groupingBy(t -> t.from, () -> new EnumMap <>(Phase.class), toMap(t -> t.to, t -> t, (x, y) -> y, () -> new EnumMap <>(Phase.class)))); public static Transition from (Phase from, Phase to) { return m.get(from).get(to); } } public static void main (String[] args) { for (Phase src : Phase.values()) { for (Phase dst : Phase.values()) { Transition transition = Transition.from(src, dst); if (transition != null ) System.out.printf("%s에서 %s로 : %s %n" , src, dst, transition); } } } }
실행결과
1 2 3 4 5 6 7 8 9 10 SOLID에서 LIQUID로 : MELT SOLID에서 GAS로 : SUBLIME LIQUID에서 SOLID로 : FREEZE LIQUID에서 GAS로 : BOIL GAS에서 SOLID로 : DEPOSIT GAS에서 LIQUID로 : CONDENSE Process finished with exit code 0
위처럼 짜면 새로 추가 되어도 문제가 없다.
그럼 추가해 보면
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 package com.github.sejoung.codetest.enumtest;import java.util.EnumMap;import java.util.Map;import java.util.stream.Stream;import static java.util.stream.Collectors.groupingBy;import static java.util.stream.Collectors.toMap;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); private final Phase from; private final Phase to; Transition(Phase from, Phase to) { this .from = from; this .to = to; } private static final Map<Phase, Map<Phase, Transition>> m = Stream.of(values()).collect(groupingBy(t -> t.from, () -> new EnumMap <>(Phase.class), toMap(t -> t.to, t -> t, (x, y) -> y, () -> new EnumMap <>(Phase.class)))); public static Transition from (Phase from, Phase to) { return m.get(from).get(to); } } public static void main (String[] args) { for (Phase src : Phase.values()) { for (Phase dst : Phase.values()) { Transition transition = Transition.from(src, dst); if (transition != null ) System.out.printf("%s에서 %s로 : %s %n" , src, dst, transition); } } } }
실행결과
1 2 3 4 5 6 7 8 9 10 11 SOLID에서 LIQUID로 : MELT SOLID에서 GAS로 : SUBLIME LIQUID에서 SOLID로 : FREEZE LIQUID에서 GAS로 : BOIL GAS에서 SOLID로 : DEPOSIT GAS에서 LIQUID로 : CONDENSE GAS에서 PLASMA로 : IONIZE PLASMA에서 GAS로 : DEIONIZE Process finished with exit code 0
참조