[Effective Java] 37. ordinal 인덱싱 대신 EnumMap을 사용하라

최강일·2024년 8월 14일
0

Effective Java

목록 보기
15/17

ordinal()이란?

enum 요일 {,,,,,,}

public class Example {
    public static void main(String[] args) {
        System.out.println(요일..ordinal()); // 출력: 0
        System.out.println(요일..ordinal()); // 출력: 2
        System.out.println(요일..ordinal()); // 출력: 6
    }
}

나쁜 예제

public class Plant {
 
    enum LifeCycle { ANNUAL, PERNNIAL, 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;
    }
}

ordinal 인덱싱 사용

public class OrdinalIndexingMain {
    public static void main(String[] args) {
        List<Plant> garden = List.of(
                new Plant("A", Plant.LifeCycle.ANNUAL),
                new Plant("B", Plant.LifeCycle.BIENNIAL),
                new Plant("C", Plant.LifeCycle.PERENNIAL),
                new Plant("D", Plant.LifeCycle.ANNUAL));

        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 plant : garden) {
            plantsByLifeCycle[plant.lifeCycle.ordinal()].add(plant);
        }

        // 결과 출력
        for (int i = 0; i < plantsByLifeCycle.length; i++) {
            System.out.printf("%s: %s%n", Plant.LifeCycle.values()[i], plantsByLifeCycle[i]);
        }
    }
}

배열에서 인덱스를 얻고자 한다면 ordinal을 종종 사용할 수 있다.
그런데 ordinal 인덱싱은 좋지않다.

ordinal을 통한 배열 인덱싱은 사실상 내부적으로 많은 문제를 야기한다.
해당 타입의 정수 값이 정말 올바른지를 판단할 수 없어서 런타임에서나 되어야 에러를 만날 수 있다.

  • 배열은 제네릭과 호환되지 않아 비검사 형변환을 수행해야한다. ((Set\[]) 에서 컴파일 경고)
  • 유효한 인덱스여야한다 : 잘못하면 ArrayIndexOutOfBoundException

이런 상황에서는 EnumMap을 사용하자. EnumMap은 쉽게 말해 Enum의 값을 Key로 갖는 Map이다. Map의 키가 Enum이므로 그 값의 범위와 상수를 보장해주어 더욱 더 안전하다.

해결책 : EnumMap

Map<LifeCycle, Set<Plant>> plantsByLifeCycle = new EnumMap<>(LifeCycle.class);
 
for (LifeCycle lifeCycle : LifeCycle.values()) {
    plantsByLifeCycle.put(lifeCycle,new HashSet<>());
}
 
for (Plant plant : garden) {
    plantsByLifeCycle.get(plant.lifeCycle).add(plant);
}
 
//EnumMap은 toString을 재정의
System.out.println(plantsByLifeCycle);
  • 형변환을 하지 않음 -> EnumMap은 제네릭과 잘 동작하기에 불필요한 형변환 스킵
  • EnumMap의 toString을 이용하여 출력
  • ordinal을 이용해 배열 인덱스를 사용하지 않아 인덱스를 계산하는 과정에서 오류가 발생하지 않음
  • EnumMap은 내부에서 배열을 사용하기 때문에 Map의 타입 안전성과 배열의 성능을 보장 -> EnumMap은 키로 사용되는 Enum 타입을 알고 있어서, 잘못된 Enum 값을 사용하면 컴파일 시점에 오류를 발견할 수 있다.

결론

ordinal을 사용한 인덱싱은 여러 위험을 내포하고 있어 피하는 것이 좋고, 대신 EnumMap을 사용하면 더 안전하고 유지보수하기 쉬운 코드를 작성할 수 있다.

profile
Search & Backend Engineer

0개의 댓글