Item 37. EnumMap

ordinal 인덱싱 λŒ€μ‹  EnumMap을 μ‚¬μš©ν•˜λΌ.

λ°°μ—΄μ΄λ‚˜ λ¦¬μŠ€νŠΈμ—μ„œ μ›μ†Œλ₯Ό κΊΌλ‚Ό λ•Œ ordinal λ©”μ„œλ“œλ‘œ 인덱슀λ₯Ό 얻을 수 μžˆμ§€λ§Œ, 보톡 이런 μš©λ„λ‘œ ordinal을 μ“°λŠ” 것은 쒋지 μ•Šλ‹€.

ordinal

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;
    }
}

class Main {
    public static void main(String[] args) {
        Set<Plant>[] plantsByLifeCycle = (Set<Plant>[]) new Set[Plant.LifeCycle.values().length]; // 비검사 ν˜•λ³€ν™˜ κ²½κ³ 
        List<Plant> garden = List.of(
                new Plant("λ°”μ§ˆ", Plant.LifeCycle.ANNUAL),
                new Plant("μΊλŸ¬μ›¨μ΄", Plant.LifeCycle.BIENNIAL),
                new Plant("λ”œ", Plant.LifeCycle.ANNUAL),
                new Plant("라벀더", Plant.LifeCycle.PERENNIAL),
                new Plant("파슬리", Plant.LifeCycle.BIENNIAL),
                new Plant("둜즈마리", Plant.LifeCycle.PERENNIAL)
        );

        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++) {
            // 배열은 인덱슀의 의미λ₯Ό λͺ¨λ₯΄κΈ° λ•Œλ¬Έμ— 좜λ ₯ν•  λ•Œ λ§ˆλ‹€ LifeCycle.values()λ₯Ό ν˜ΈμΆœν•΄μ•Ό ν•œλ‹€.
            System.out.printf("%s: %s%n", Plant.LifeCycle.values()[i], plantsByLifeCycle[i]); // ArrayIndexOutOfBoundsException λ°œμƒ κ°€λŠ₯
        }
    }
}

EnumMap

μœ„ 방식은 λ™μž‘μ€ ν•˜μ§€λ§Œ 주석에 적힌 λŒ€λ‘œ λ¬Έμ œκ°€ λ§Žλ‹€. μœ„ 방식이 μ•„λ‹Œ EnumMap을 μ‚¬μš©ν•˜λ©΄ 이런 문제λ₯Ό ν•΄κ²°ν•  수 μžˆλ‹€.

class Main {
    public static void main(String[] args) {
        Map<Plant.LifeCycle, Set<Plant>> plantsByLifeCycle = new EnumMap<>(Plant.LifeCycle.class); // ν˜•λ³€ν™˜ 없이 μ•ˆμ „ν•˜κ²Œ μ„ μ–Έ

        List<Plant> garden = List.of(
                new Plant("λ°”μ§ˆ", Plant.LifeCycle.ANNUAL),
                new Plant("μΊλŸ¬μ›¨μ΄", Plant.LifeCycle.BIENNIAL),
                new Plant("λ”œ", Plant.LifeCycle.ANNUAL),
                new Plant("라벀더", Plant.LifeCycle.PERENNIAL),
                new Plant("파슬리", Plant.LifeCycle.BIENNIAL),
                new Plant("둜즈마리", Plant.LifeCycle.PERENNIAL)
        );

        for (Plant.LifeCycle lc : Plant.LifeCycle.values()) {
            plantsByLifeCycle.put(lc, new HashSet<>());
        }

        for (Plant p : garden) {
            plantsByLifeCycle.get(p.lifeCycle).add(p);
        }

        for (Plant.LifeCycle lifeCycle : plantsByLifeCycle.keySet()) {
            // ν‚€λ₯Ό 직접 μ‚¬μš©ν•΄ μˆœνšŒν•˜λ©΄ ordinal λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜μ§€ μ•Šμ•„λ„ λœλ‹€.
            System.out.printf("%s: %s%n", lifeCycle, plantsByLifeCycle.get(lifeCycle)); // 인덱슀λ₯Ό μ‚¬μš©ν•˜μ§€ μ•Šκ³ λ„ 좜λ ₯ κ°€λŠ₯
        }
    }
}

이와 같이 EnumMap을 μ‚¬μš©ν•˜λ©΄ μ„±λŠ₯ μ €ν•˜λ„ μ—†κ³ , νƒ€μž… μ•ˆμ „μ„±λ„ 확보할 수 μžˆλ‹€. (λ‚΄λΆ€μ—μ„œ 배열을 μ‚¬μš©ν•˜κΈ° λ•Œλ¬Έμ΄λ©°, λ‚΄λΆ€ κ΅¬ν˜„ 방식을 μ•ˆμœΌλ‘œ μˆ¨κ²¨μ„œ νƒ€μž… μ•ˆμ „μ„±μ„ ν™•λ³΄ν•œλ‹€.)

μ—΄κ±° νƒ€μž… κ°’λ“€ 맀핑 μ˜ˆμ‹œ

μ΄λ²ˆμ—” 두 μ—΄κ±° νƒ€μž… 값듀을 λ§€ν•‘ν•˜μ—¬ λ‘œμ§μ„ κ΅¬ν˜„ν•΄μ•Όν•˜λŠ” 경우λ₯Ό μ‚΄νŽ΄λ³΄λ©΄ μ•„λž˜μ™€ 같이 κ΅¬ν˜„ν•  수 μžˆλ‹€.

enum Phase {
    SOLID, LIQUID, GAS;

    public enum Transition {
        MELT, FREEZE, BOIL, CONDENSE, SUBLIME, DEPOSIT;

        // ν–‰: from, μ—΄: to, μƒνƒœκ°€ λŠ˜μ–΄λ‚˜λŠ” 만큼 이차원 배열이 컀진닀.
        private static final Transition[][] TRANSITIONS = {
                {null, MELT, SUBLIME},
                {FREEZE, null, BOIL},
                {DEPOSIT, CONDENSE, null}
        };

        // λ‹€λ₯Έ μƒνƒœλ‘œ μ „μ΄ν•˜λŠ” λ©”μ„œλ“œ
        public static Transition from(Phase from, Phase to) {
            // ordinal λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•΄ 인덱슀λ₯Ό μ–»λŠ”λ‹€, μ΄λŠ” μœ„μ—μ„œ μ‚΄νŽ΄λ³Έ κ²ƒμ²˜λŸΌ 쒋지 μ•Šμ€ 방식이닀.
            return TRANSITIONS[from.ordinal()][to.ordinal()];
        }
    }
}

μœ„μ™€ 같이 κ΅¬ν˜„ν•˜λ©΄ μƒνƒœκ°€ λŠ˜μ–΄λ‚  λ•Œλ§ˆλ‹€ 이차원 λ°°μ—΄μ˜ 크기가 컀지기 λ•Œλ¬Έμ— λ©”λͺ¨λ¦¬ λ‚­λΉ„κ°€ μ‹¬ν•΄μ§€λŠ” 것 뿐만 μ•„λ‹ˆλΌ 였λ₯˜κ°€ λ°œμƒν•  κ°€λŠ₯성도 높아진닀.

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 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))));

        private final Phase from;
        private final Phase to;

        Transition(Phase from, Phase to) {
            this.from = from;
            this.to = to;
        }

        public static Transition from(Phase from, Phase to) {
            return m.get(from).get(to);
        }
    }
}

ν•˜μ§€λ§Œ EnumMap을 μ‚¬μš©ν•˜λ©΄ μ΄ˆκΈ°ν™” 과정이 λ‹€μ†Œ λ³΅μž‘ν•΄μ§€μ§€λ§Œ, 이차원 배열을 μ‚¬μš©ν•˜λŠ” 것보닀 훨씬 μ•ˆμ •μ μ΄κ³  μœ μ—°ν•˜κ²Œ μ‚¬μš©ν•  수 μžˆλ‹€. λ§Œμ•½ μƒˆλ‘œμš΄ μƒνƒœμΈ PLASMAκ°€ μΆ”κ°€λ˜λ”λΌλ„ 주석과 같이 μƒνƒœμ™€ 전이 λͺ©λ‘μ—λ§Œ μΆ”κ°€ν•˜κ³ , λ‚˜λ¨Έμ§€ μ½”λ“œλŠ” μˆ˜μ •ν•  ν•„μš”κ°€ μ—†λ‹€.

Last updated