๋ฐฐ์ด์ ์ธ๋ฑ์ค๋ฅผ ์ป๊ธฐ ์ํด ordinal์ ์ฐ๋ ๊ฑฐ์ ์ผ๋ฐ์ ์ผ๋ก ์ข์ง ์์ผ๋, ๋์ EnumMap์ ์ฌ์ฉํ๋๋ก ํ์.
์๋์ 2๊ฐ์ง ์ํฉ(์ผ์ฐจ์ ๊ด๊ณ, ๋ค์ฐจ์ ๊ด๊ณ)์ ํตํด ์ EnumMap์ ์ฌ์ฉํด์ผ ํ๋์ง ์์๋ณด๋๋ก ํ์.
1. 1์ฐจ์ ๊ด๊ณ์์์ EnumMap
์๋์ Plant ํด๋์ค๋ฅผ ํตํด ์๋ฌผ์ ์ด๋ฆ(name)๊ณผ ์์ ์ฃผ๊ธฐ(lifeCycle)๋ฅผ ๊ฐ๋จํ ๋ํ๋ผ ์ ์๋ค.
public class Plant {
enum LifeCycle { ANNUAL, PERENNIAL, BIENNIAL } // ํํด์ด์ด, ์ฌ๋ฌํด์ด์ด, ๋ํด์ด์ด
final String name;
final LifeCycle lifeCycle;
public Plant(String name, LifeCycle lifeCycle) {
this.name = name;
this.lifeCycle = lifeCycle;
}
@Override
public String toString() {
return name;
}
}
์ด์ ์ ์์ ์ฌ์ ์๋ฌผ๋ค์ ๋ฐฐ์ด ํ๋(garden)๋ก ๊ด๋ฆฌํ๊ณ , ์ด๋ค์ ์์ ์ฃผ๊ธฐ๋ณ๋ก ๋ฌถ์ด๋ณด๊ณ ์ ํ๋ค.
ordinal()์ ๋ฐฐ์ด ์ธ๋ฑ์ค๋ก ์ฌ์ฉํ์ฌ ์ด 3๊ฐ์ ์งํฉ(ํํด์ด์ด, ์ฌ๋ฌํด์ด์ด, ๋ํด์ด์ด)์ ๋ง๋ค๊ณ , ์ ์์ ํ ๋ฐํด ๋๋ฉฐ ๊ฐ ์๋ฌผ์ ํด๋น ์งํฉ์ ๋ฃ๊ณ ์ ํ๋ค.
public static void main(String[] args) {
Set<Plant>[] plantsByLifeCycle =
(Set<Plant>[]) new Set[LifeCycle.values().length];
for (int i = 0; i < plantsByLifeCycle.length; i++)
plantsByLifeCycle[i] = new HashSet<>();
Plant[] garden = new Plant[]{new Plant("A", LifeCycle.ANNUAL),
new Plant("B", LifeCycle.PERENNIAL),
new Plant("C", LifeCycle.BIENNIAL),
new Plant("D", LifeCycle.BIENNIAL)};
// LifeCycle.ANNUAL -> 0
// LifeCycle.PERENNIAL -> 1
// LifeCycle.BIENNIAL -> 2
// ๊ฐ๊ฐ์ ์ธ๋ฑ์ค์ ์๋ฌผ ์ ์ฅ ์ ์ฅ
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]);
}
// ์ถ๋ ฅ ๊ฒฐ๊ณผ
// ANNUAL: [A]
// PERENNIAL: [B]
// BIENNIAL: [D, C]
}
์์ ์ฝ๋๋ ๋์์ ํ์ง๋ง ๋ค์๊ณผ ๊ฐ์ ๋ฌธ์ ๊ฐ ์๋ค.
- ๋ฐฐ์ด์ ์ ๋ค๋ฆญ๊ณผ ํธํ๋์ง ์์ผ๋ ๋น๊ฒ์ฌ ํ๋ณํ์ ์ํํด์ผ ํ๊ณ ๊น๋ํ ์ปดํ์ผ๋์ง ์์ ๊ฒ์ด๋ค.
- ๋ฐฐ์ด์ ๊ฐ ์ธ๋ฑ์ค์ ์๋ฏธ๋ฅผ ๋ชจ๋ฅด๋ ์ถ๋ ฅ ๊ฒฐ๊ณผ์ ์ง์ ๋ ์ด๋ธ์ ๋ฌ์์ผ ํ๋ค.
- ์ ํํ ์ ์ซ๊ฐ์ ์ฌ์ฉํ๋ค๋ ๊ฒ์ ์ฌ๋ฌ๋ถ์ด ์ง์ ๋ณด์ฆํด์ผ ํ๋ค. (์ ์๋ ์ด๊ฑฐ ํ์ ๊ณผ ๋ฌ๋ฆฌ ํ์ ์์ ํ์ง ์๊ธฐ ๋๋ฌธ์ด๋ค.)
EnumMap์ ์ฌ์ฉํ๋ฉด ๋ฐฐ์ด์ด ์ค์ง์ ์ผ๋ก ์ด๊ฑฐ ํ์
์์๋ฅผ ๊ฐ์ผ๋ก ๋งคํ์ ํ๊ฒ ๋๋ค. ํ๋ฅญํ ํด๊ฒฐ์ฑ
์ด๋ค.
EnumMap์ ์ฌ์ฉํด ๋ฐ์ดํฐ์ ์ด๊ฑฐ ํ์
์ ๋งคํํ๋ฉด ์๋์ ๊ฐ๋ค.
public static void main(String[] args) {
Map<Plant.LifeCycle, Set<Plant>> plantsByLifeCycle =
new EnumMap<>(Plant.LifeCycle.class);
for (Plant.LifeCycle lc : Plant.LifeCycle.values())
plantsByLifeCycle.put(lc, new HashSet<>());
Plant[] garden = new Plant[]{new Plant("A", LifeCycle.ANNUAL),
new Plant("B", LifeCycle.PERENNIAL),
new Plant("C", LifeCycle.BIENNIAL),
new Plant("D", LifeCycle.BIENNIAL)};
for(Plant p : garden)
plantsByLifeCycle.get(p.lifeCycle).add(p);
System.out.println(plantsByLifeCycle);
// ์ถ๋ ฅ ๊ฒฐ๊ณผ
// {ANNUAL=[A], PERENNIAL=[B], BIENNIAL=[D, C]}
}
ordinal()์ ๋ฐฐ์ด ์ธ๋ฑ์ค๋ก ์ฌ์ฉํ์ ๋์ ๋ฌธ์ ์ ์ EnumMap์ ์ฌ์ฉํ๋ฉด ๋ค์๊ณผ ๊ฐ์ด ํด๊ฒฐํ๊ฒ ๋๋ค.
- ์์ ํ์ง ์์ ํ๋ณํ์ ์ฐ์ง ์๋๋ค.
- ๋งต์ ํค์ธ ์ด๊ฑฐ ํ์ ์ด ๊ทธ ์์ฒด๋ก ์ถ๋ ฅ์ฉ ๋ฌธ์์ด์ ์ ๊ณตํ๋ ์ถ๋ ฅ ๊ฒฐ๊ณผ์ ์ง์ ๋ ์ด๋ธ์ ๋ฌ ์ผ์ด ์๋ค.
- ๋ฐฐ์ด ์ธ๋ฑ์ค๋ฅผ ๊ณ์ฐํ๋ ๊ณผ์ ์์ ์ค๋ฅ๊ฐ ๋ ๊ฐ๋ฅ์ฑ์ด ์์ฒ๋ด์๋๋ค.
EnumMap๋ ๊ทธ ๋ด๋ถ์์ ๋ฐฐ์ด์ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ์ฑ๋ฅ์ด ordinal์ ์ด ๋ฐฐ์ด์ ๋น๊ฒฌ๋๋ค. ๋ด๋ถ ๊ตฌํ ๋ฐฉ์์ ์์ผ๋ก ์จ๊ฒจ์ Map์ ํ์ ์์ ์ฑ๊ณผ ๋ฐฐ์ด์ ์ฑ๋ฅ์ ๋ชจ๋ ์ป์ด๋ธ ๊ฒ์ด๋ค.
์คํธ๋ฆผ์ ์ฌ์ฉํ๋ฉด ์๋์ ๊ฐ์ด ์ฝ๋๋ฅผ ๋ ๊ฐ๋จํ๊ฒ ๋ง๋ค ์ ์๋ค.
public static void main(String[] args) {
Plant[] garden = new Plant[]{new Plant("A", LifeCycle.ANNUAL),
new Plant("B", LifeCycle.PERENNIAL),
new Plant("C", LifeCycle.BIENNIAL),
new Plant("D", LifeCycle.BIENNIAL)};
System.out.println(Arrays.stream(garden)
.collect(groupingBy(p -> p.lifeCycle)));
// ์ถ๋ ฅ ๊ฒฐ๊ณผ
// {ANNUAL=[A], BIENNIAL=[C, D], PERENNIAL=[B]}
}
๊ทธ๋ฌ๋ ์ด ์ฝ๋๋ EnumMap์ด ์๋ ๊ณ ์ ํ ๋งต ๊ตฌํ์ฒด๋ฅผ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ EnumMap์ ์จ์ ์ป์ ๊ณต๊ฐ๊ณผ ์ฑ๋ฅ ์ด์ ์ด ์ฌ๋ผ์ง๋ค.
๊ทธ๋์ ์๋์ ๊ฐ์ด mapFactory ๋งค๊ฐ๋ณ์์ ์ํ๋ ๋งต ๊ตฌํ์ฒด๋ฅผ ๋ช ์ํ์ฌ EnumMap์ ์ฌ์ฉํ ์ ์๋ค.
public static void main(String[] args) {
Plant[] garden = new Plant[]{new Plant("A", LifeCycle.ANNUAL),
new Plant("B", LifeCycle.PERENNIAL),
new Plant("C", LifeCycle.BIENNIAL),
new Plant("D", LifeCycle.BIENNIAL)};
System.out.println(Arrays.stream(garden)
.collect(groupingBy(p -> p.lifeCycle,
() -> new EnumMap<>(LifeCycle.class), toSet())));
// ์ถ๋ ฅ ๊ฒฐ๊ณผ
// {ANNUAL=[A], PERENNIAL=[B], BIENNIAL=[D, C]}
}
2. ๋ค์ฐจ์ ๊ด๊ณ์์์ EnumMap
๋ ์ด๊ฑฐ ํ์ ๊ฐ๋ค์ ๋งคํํ๊ธฐ ์ํด ordinal์ ๋ ๋ฒ์ด๋ ์ด ๋ฐฐ์ด๋ค์ ๋ฐฐ์ด์ด ์๋ค.
์์๋ก๋ ๋ ๊ฐ์ง ์ํ(Phase)๋ฅผ ์ ์ด(Transition)์ ๋งคํํ๋๋ก ๊ตฌํํ ํ๋ก๊ทธ๋จ์ด ์๋ค.
(์ก์ฒด → ๊ณ ์ฒด = ๊ณ ํ, ๊ณ ์ฒด → ์ก์ฒด = ์กํ)
public enum Phase {
SOLID, LIQUID, GAS;
public enum Transition {
MELT, FREEZE, BOIL, CONDENSE, SUBLIME, DEPOSIT;
// ํ์ from์ ordinal์, ์ด์ to์ ordinal์ ์ธ๋ฑ์ค๋ก ์ด๋ค.
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()];
}
}
}
์ด๋ ๊ฒ ordinal()์ ๋ฐฐ์ด ์ธ๋ฑ์ค๋ก ์ฌ์ฉํ๋ฉด ๋ฌธ์ ๊ฐ ๋ง๋ค.
- ์ปดํ์ผ๋ฌ๋ ordinal๊ณผ ๋ฐฐ์ด ์ธ๋ฑ์ค์ ๊ด๊ณ๋ฅผ ์ ๋๋ฆฌ๊ฐ ์๋ค.
- ArrayIndexOutOfBoundsException์ด๋ NullPointerException์ ๋์ง ์ ์๋ค.
- ์์ ์ด ํ์ ํฌ๊ธฐ๋ ์ํ์ ๊ฐ์ง์๊ฐ ๋์ด๋๋ฉด ์ ๊ณฑํด์ ์ปค์ง๋ฉฐ, null๋ก ์ฑ์์ง๋ ์นธ๋ ๋์ด๋๋ค.
์ ์ด ํ๋๋ฅผ ์ป์ผ๋ ค๋ฉด ์ด์ ์ํ(from)์ ์ดํ ์ํ(to)๊ฐ ํ์ํ๋, ๋งต 2๊ฐ๋ฅผ ์ค์ฒฉํ๋ฉด ์ฝ๊ฒ ํด๊ฒฐํ ์ ์๋ค. ์์ชฝ ๋งต์ ์ด์ ์ํ์ ์ ์ด๋ฅผ ์ฐ๊ฒฐํ๊ณ ๋ฐ๊นฅ ๋งต์ ์ดํ ์ํ์ ์์ชฝ ๋งต์ ์ฐ๊ฒฐ ํ๋ฉด ๋๋ค.
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);
}
}
}
'Map<Phase, Map<Phase, Transition>>'์ "์ด์ ์ํ์์ '์ดํ ์ํ์์ ์ ์ด๋ก์ ๋งต'์ ๋์์ํค๋ ๋งต"์ด๋ผ๋ ๋ป์ด๋ค.
๋ง์ฝ ์ฌ๊ธฐ์ ์๋ก์ด ์ํ์ธ ํ๋ผ์ค๋ง(PLASMA)๋ฅผ ์ถ๊ฐํ๋ค๊ณ ์๊ฐํด ๋ณด์.
๊ทธ๋ฌ๋ฉด ์ํ ๋ชฉ๋ก์ PLASMA๋ฅผ ์ถ๊ฐํ๊ณ , ์ ์ด ๋ชฉ๋ก์ IONIZE(GAS, PLASMA)์ DEIONIZE(PLASMA, GAS)๋ง ์ถ๊ฐํ๋ฉด ๋์ด๋ค. ๊ฐ๋จํ๋ค.
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);
...
}
}
์ค์ ๋ด๋ถ์์๋ ๋งต๋ค์ ๋งต์ด ๋ฐฐ์ด๋ค์ ๋ฐฐ์ด๋ก ๊ตฌํ๋๋ ๋ญ๋น๋๋ ๊ณต๊ฐ๊ณผ ์๊ฐ๋ ๊ฑฐ์ ์์ด ๋ช ํํ๊ณ ์์ ํ๊ณ ์ ์ง๋ณด์ํ๊ธฐ ์ข๋ค.
ํด๋น ๊ธ์ Joshua Bloch ๋์ 'Effective Java 3/E'๋ฅผ ์ฐธ๊ณ ํ์์ต๋๋ค.