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);
// 결과 출력
for (int i = 0; i < plantsByLifeCycle.length; i++) {
System.out.printf("%s: %s%n",
Plant.LifeCycle.values()[i], plantsByLifeCycle[i]);
}
배열은 제네릭과 호환되지 않아 비검사 형변환을 수행해야 함 -> 깔끔하게 컴파일되지 않음
배열은 각 인덱스의 의미를 모르니 출력 결과에 직접 레이블을 달아야 함
정확한 정숫값을 사용한다는 것을 직접 프로그래머가 보증해야 함. 정수는 열거 타입과 달리 타입 안전하지 않기 때문
Map<Plant.LifeCycle, Set<Plant» plantsByLifeCycle =
new EnumMapo(Plant.LifeCycle.class);
for (Plant.LifeCycle lc : Plant.LifeCycle.values())
plantsByLifeCycle. put (Ic, new HashSetoO);
for (Plant p : garden)
plantsByLifeCycle.get(p.lifeCycle).add(p);
System.out.printIn(plantsByLifeCycle);
EnumMap의 성능이 ordinal을 쓴 배열에 비견되는 이유는?
- 그 내부에서 배열을 사용하기 때문
- 내부 구현 방식을 안으로 숨겨서 Map의 타입 안전성과 배열의 성능을 모두 얻어냄
stream을 활용하면 코드를 더 줄일 수 있음
스트림을 사용하면 EnumMap만 사용했을 때와는 살짝 다르게 동작함
EnumMap 버전은 언제나 식물의 생애주기당 하나씩의 중첩 맵을 만들음
스트림 버전에서는 해당 생애주기에 속하는 식물이 있을 때만 만들음
예컨대 정원에 한해살이와 여러해살이 식물만 살고 두해살이는 없다.
EnumMap 버전에서는 맵을 3개 만들고 스트림 버 전에서는 2개만 만든다.
public enum Phase {
SOLID, LIQUID, GAS;
public enum Transition {
MELT, FREEZE, BOIL, CONDBJSE, SUBLIME, DEPOSIT;
// 행은 from의 ordinal!, 열은 to의 ordinal올 인덱스로 쓴다.
private static final Transition[][] TRANSITIONS = {
{ null, MELT, SUBLIME },
{ FREEZE, null, BOIL },
{ DEPOSIT, CONDBJSE, null }
};
// 한 상태에서 다른 상태로의 전이를 반환한다.
public static Transition from(Phase from, Phase to) {
return TRANSITIONS[from.ordinaI()] [to.ordinaK)];
}
}
}
멋져 보이지만 겉모습에 속으면 안 된다.
컴파일러는 ordinal과 배열 인텍스의 관계를 알 도리가 없다.
상전이 표의 크기는 상태의 가짓수가 늘어나면 제곱해서 커지며 null로 채워지는 칸도 늘어날 것이다.
public enum Phase {
SOLID, LIQUID, GAS;
public enum Transition {
MELT(SOLID, LIQUID), FREEZE(LIQUID, SOLID),
BOIL(LIQUID, GAS), 〔OND며SE(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(g roupingBy(t -> t.from,
() -> new EnumMap<>(Phase.class),
toMap(t -> t.to, t -> t,
(x, y) -> y, 0 -> new EnumMapo(Phase.class))));
public static Transition from(Phase from, Phase to) {
return m.get(from).get(to);
}
}
}
상전이 맵을 초기화하는 코드는 제법 복잡함
이 맵의 타입인 Map<Phase, Map<Phase, Transition>>은 “이전 상태에서 '이후 상태 에서 전이로의 맵’에 대응시키는 맵”이라는 뜻이다.