Java

EnumMap 살펴보기

EnumMap 

EnumMap 클래스는 Map 구현체 중 Enum type 을 키로 사용하는 클래스입니다. HashMap 과 비교해봤을 때, EnumMap은 성능상의 이점을 노릴 수 있습니다. 그 이유를 살펴보겠습니다.

데이터 저장과정

생성자로 key type(enum) 을 넘겨주면, enum 에 정의된 열거형 상수의 개수만큼 내부적으로 Object 배열을 생성합니다.

따라서 null 을 허용하지 않습니다. 하지만 get 이나 remove 를 호출할때 null 을 넣어도 예외를 발생시키지 않습니다.

public enum NumberEnum {
	ONE, TWO, THREE
}

class EnumTest {
	@Test
    void enum_test() {
      Map<String, String> map = new HashMap<>();
	  map.put(null, "test");
	  assertEquals("test", map.get(null));

      Map<NumberEnum, String> enumMap = new EnumMap<>(NumberEnum.class);
      enumMap.put(NumberEnum.ONE, "one");
      assertEquals("one", enumMap.get(NumberEnum.ONE));
      
      assertThrows(NullPointerException.class, () -> enumMap.put(null, "enum-test"));
      assertDoesNotThrow(() -> enumMap.get(null));
	  assertDoesNotThrow(() -> enumMap.remove(null));
    }
}

enum 에 정의된 열거형 상수의 개수만큼 내부적으로 Object 배열의 크기를 정합니다(vals 변수)

put 할때마다 열거형 상수의 정렬순서(ordinal)을 index 로 배열에 value 를 할당합니다. 이때, 원래 값이 없었다면 size를 증가시킴으로써 외부에서 size() 메서드를 호출하였을 때, 정상적인 값이 표출되도록 합니다.

성능상 이점

서두에서 HashMap 에 비해 성능상에 이점을 얻는 다고 했는 데, HashMap 은 hash 값을 계산하여 table 을 제어하는 형태로 데이터를 관리합니다. 이에 비해 EnumMap 은 열거형 상수가 정의된 순서를 가지고, 배열의 index 만 가져오면 되기 때문에 상대적으로 대부분의 경우에 성능이 더 좋다고 할 수 있습니다.

동기화

EnumMap 은 HashMap 과 같이 thread safe 하지 않습니다. 따라서 멀티쓰레드 환경에서 여러 쓰레드가 접근할 수 있다면, 동기화를 위한 처리를 해주어야 합니다. 이를 위해 SynchronizedMap 을 사용할 수 있습니다. SynchronizedMap 은 Collections 에 내부클래스로 선언되어있으며, public 메서드에 대해 모두 synchronized 키워드로 래핑해둔 클래스입니다. ConcurrentHashMap 과 비교해봤을 때, 모든 메서드를 동기화처리 하였기 때문에, 속도저하가 있을 수 있습니다.

또다른 활용

함수형 인터페이스를 정의하여 활용하는 방법을 살펴보겠습니다. 예전에 프로젝트를 진행할 때, 외부의 파라미터에 따라 특정 메소드를 캐싱해두고 쓰고 싶다는 생각이 들어 사용하게 되었습니다. 

@Test
@DisplayName("함수 테스트")
void enum_function() {
  Map<NumberEnum, Function<String, Integer>> enumMap = new EnumMap<>(NumberEnum.class);
  enumMap.put(NumberEnum.ONE, s -> Integer.parseInt(s) + 1);
  enumMap.put(NumberEnum.TWO, s -> Integer.parseInt(s) + 2);
  enumMap.put(NumberEnum.THREE, s -> Integer.parseInt(s) + 3);

  assertEquals(2, enumMap.get(NumberEnum.ONE).apply("1"));
  assertEquals(3, enumMap.get(NumberEnum.TWO).apply("1"));
  assertEquals(4, enumMap.get(NumberEnum.THREE).apply("1"));
}

이와 같은 형태로 정의하여 enum type 에 따라 다른 함수를 실행하도록 할 수 있습니다. 정의하는 곳마다 다른 함수를 넘겨줄 수도 있습니다.

사실 저런 형태로 사용할 때는 잘 몰랐지만, Enum 안에 메소드를 정의해서 사용하는 방법을 더 많이 쓰는 거 같습니다.

Enum 에 메소드를 정의해서 활용 참고자료

'Java' 카테고리의 다른 글

PriorityQueue 살펴보기  (0) 2021.01.04
정렬을 돕는 Comparable, Comparator  (0) 2020.12.18
resource file 읽기  (0) 2018.12.18
eclipse task tag 사용(TODO)  (0) 2018.12.18
java applicaiton logback 설정  (0) 2018.12.18