ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 아이템 37. ordinal 인덱싱 대신 EnumMap을 사용하라.
    JAVA/Effective java 2021. 3. 28. 16:12
    • 배열이나 리스트에서 원소를 꺼낼 때 ordinal 메서드로 인덱스를 얻는 코드가 있다.
    • 다음 클래스를 예로 살펴보자.

     

    • 정원에 심은 식물을 배열 하나로 관리하고, 이들을 생애주기 (한해살이, 여러해살이, 두해살이) 별로 묶어보자.
    • 생애주기별로 총 3개의 집합을 만들고 정원을 한 바퀴 돌며 각 식물을 해당 집합에 넣는다.
    • 이때 집합들을 배열 하나에 넣고 생애주기의 ordinal 값을 그 배열의 인덱스로 사용한 예를 봐보자.

    • 동작은 하지만 문제가 많다.
    • 배열을 제네릭과 호환되지 않으니 비검사 형 변환을 수행해야 하고 깔끔히 컴파일되지 않을 것이다.
    • 배열은 각 인덱스의 의미를 모르니 출력 결과에 직접 레이블을 달아야 한다.
    • 가장 심각한 문제는 정확한 정숫값을 사용한다는 것을 직접 보증해야 한다는 점이다.
    • 정수는 열거타입과 달리 타입 안전하지 않기에 잘못된 동작을 그냥 수행하거나 Exception을 던질 것이다.

     

     

    • 사실 열거 타입을 키로 사용하도록 설계한 아주 빠른 Map 구현체가 존재하는데, 바로 EnumMap이 그 주인공이다.
    • 다음은 EnumMap을 사용해 개선한 코드다.

    • 더 짧고 명료하며 안전하고 성능도 원래 버전과 비등하다.
    • 안전하지 않은 형변환은 쓰지 않고, 맵의 키인 열거 타입이 그 자체로 출력용 문자열을 제공하니 출력 결과에 직접 레이블을 달 일도 없다.

     

     

    • 또한 배열 인덱스를 계산하는 과정에서 오류가 날 가능성도 봉쇄된다.
    • EnumMap의 성능이 ordinal을 쓴 배열에 비견되는 이유는 그 내부에서 배열을 사용하기 때문이다.
    • 내부 구현 방식을 안으로 숨겨서 Map의 타입 안전성과 배열의 성능을 모두 얻어낸 것이다.
    • 여기서 EnumMap의 생성자가 받는 키 타입의 Class 객체는 한정적 타입 토큰으로, 런타임 제네릭 타입 정보를 제공한다.

     

     

    • 스트림을 사용해 맵을 관리 하면 코드를 더 줄일 수 있다.

     

     


     

     

     

    • 이 코드는 앞선 정원 예제와 마찬가지로 컴파일러는 ordinal과 배열 인덱스의 관계를 알 도리가 없다.
    • 즉 Phase나 Phase.Transition 열거 타입을 수정하면서 상전이 표 TRANSITIONS를 함께 수정하지 않거나 실수로 잘못 수정하면 런타임 오류가 날 것이다.
    • Array IndexOutOfBoundsException 이나 NullPointerException을 던질 수도 있고 예외도 던지지 않고 이상하게 동작할 수도 있다.

     

     

    • 다시 이야기하면 EnumMap을 사용하는 편이 훨씬 낫다.
    • 전이 하나를 얻으려면 이전 상태(from)와 이후 상태(to)가 필요하니, 맵 2개를 중첩하면 쉽게 해결할 수 있다.
    • 안쪽 맵은 이전 상태와 전이를 연결하고 바깥 맵은 이후 상태와 안쪽 맵을 연결한다.
    • 전이 이후의 두 상태를 전이 열거 타입 Transition의 입력으로 받아, 이 Transition 상수들로 중첩된 EnumMap을 초기화하면 된다.

     

    • 이제 새로운 플라스마(PLASMA)를 추가해보자.
    • 이 상태와 연결된 전이는 2개다. 첫 번째는 기체에서 플라스마로 변하는 이온화(IONIZE) 이고, 둘째는 플라스마에서 기체로 변하는 탈이온화(DEIONIZE) 이다. 
    • EnumMap 버전에는 상태 목록에 PLASMA를 추가하고 전이 목록에 IONIZE(GAS, PLASMA)와 DEIONIZE(PLASMA, GAS)만 추가하면 끝이다.

     

     

     

     

     

     

     

    핵심 정리

     


    • 배열의 인덱스를 얻기 위해 ordinal을 쓰는 것은 일반적으로 좋지 않으니, 대신 EnumMap을 사용하라.
    • 다차원 관계는 EnumMap<..., EnumMap<...>> 로 표현하라.

     

     

Designed by Tistory.