Enum 성능 최적화

컴공생의 코딩 일기·2024년 12월 12일
2

자바

목록 보기
12/12

Enum에서 특정 문자열을 기준으로 해당하는 Enum 값을 찾는 메서드를 자주 사용합니다. 예를 들어, find 또는findByValue와 같은 메서드를 통해 문자열 값과 Enum 값을 매칭합니다. Stream API를 사용하여 Enum 값을 찾는 방식은 간단하고 직관적이지만, 반복적으로 호출될 경우 성능 상의 문제가 발생할 수 있습니다. 이에 Jay 님이 작성한 Enum 최적화 방법을 참고하여, HashMap을 사용하여 값을 캐싱하고 빠르게 찾는 방법을 기록하고자 합니다

Enum에서 특정 값(예: 문자열)을 기준으로 Enum 인스턴스를 찾는 방법 3가지 예시를 보여드리겠습니다. 각각의 방법은 Stream과 HashMap을 활용한 구현으로 구성됩니다.

Arrays.stream() 사용

@RequiredArgsConstructor
@Getter
public enum Status {
  ACTIVE("활성화"),
  INACTIVE("비활성"),
  SUSPENDED("정지"),
  UNKNOWN("알수없음");

  private final String description;

  public static Status find(String description) {
    return Arrays.stream(values())
        .filter(value -> value.description.equals(description))
        .findAny()
        .orElse(Status.UNKNOWN);
  }
}

Stream.of() 사용

@RequiredArgsConstructor
@Getter
public enum Status {
  ACTIVE("활성화"),
  INACTIVE("비활성"),
  SUSPENDED("정지"),
  UNKNOWN("알수없음");

  private final String description;

  public static Status find(String description) {
    return Stream.of(values())
        .filter(value-> value.description.equals(description))
        .findAny()
        .orElse(Status.UNKNOWN);
  }
}

HashMap 사용 → 가장 빠름

@RequiredArgsConstructor
@Getter
public enum Status {
  ACTIVE("활성화"),
  INACTIVE("비활성"),
  SUSPENDED("정지"),
  UNKNOWN("알수없음");

  private final String description;

  private static final Map<String, Status> descriptions = Collections.unmodifiableMap(Stream.of(values()).collect(Collectors.toMap(Status::getDescription, Function.identity())));

  public static Status find(String description) {
    return Optional.ofNullable(descriptions.get(description)).orElse(UNKNOWN);
  }
}

위 코드는 다음과 같은 방식으로 동작합니다:

  1. 불변 Map 생성

    descriptionsdescription을 키로, Status를 값으로 하는 불변 Map입니다. 이를 생성하기 위해 Collectors.toMap을 사용하고, 외부에서 수정할 수 없도록 Collections.unmodifiableMap으로 감쌉니다.

  2. Collectors.toMap 사용

    • 첫 번째 매개변수는 Status::getDescription으로, 각 Statusdescription 값을 키로 사용합니다.
    • 두 번째 매개변수는 Function.identity()로, 각 Status 인스턴스 자체를 값으로 사용합니다.

성능 측정

각각의 메서드에서 동일한 값(예: 특정 문자열)을 기준으로 Enum 값을 찾는 작업을 1억 번 반복하여 성능을 측정하였습니다.

@Test
  void enum_속도_측정_stream_of_이용() {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    for (int i = 0; i < 100000000; i++) {
      Status.findWithStreamOf("활성화");
    }
    for (int i = 0; i < 100000000; i++) {
      Status.findWithStreamOf("unexpected value");
    }

    stopWatch.stop();
    System.out.println("values: " + Status.findWithStreamOf("활성화"));
    System.out.println("Elapsed Time (Stream.of 이용) : " + stopWatch.getTotalTimeSeconds() + "s");
  }

  @Test
  void enum_속도_측정_array_stream_이용() {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    for (int i = 0; i < 100000000; i++) {
      Status.findWithArraysStream("활성화");
    }
    for (int i = 0; i < 100000000; i++) {
      Status.findWithArraysStream("unexpected value");
    }

    stopWatch.stop();
    System.out.println("values: " + Status.findWithArraysStream("활성화"));
    System.out.println("Elapsed Time (Stream.of 이용) : " + stopWatch.getTotalTimeSeconds() + "s");
  }

  @Test
  void enum_속도_측정_hash_map_이용() {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    for (int i = 0; i < 100000000; i++) {
      Status.findWithHashMap("활성화");
    }
    for (int i = 0; i < 100000000; i++) {
      Status.findWithHashMap("unexpected value");
    }

    stopWatch.stop();
    System.out.println("values: " + Status.findWithHashMap("활성화"));
    System.out.println("Elapsed Time (Stream.of 이용) : " + stopWatch.getTotalTimeSeconds() + "s");
  }

측정 결과

성능 결과를 보면, Array.streamStreams.of 방식은 속도 차이가 미미했으나, HashMap을 사용한 방식은 두 방법에 비해 약 10배 이상 빠른 결과를 보였습니다. 다음 프로젝트 부터 Enum 사용 시 HashMap 사용을 고려해봐도 좋을 거 같습니다!

Reference

https://pjh3749.tistory.com/279

profile
더 좋은 개발자가 되기위한 과정

0개의 댓글

관련 채용 정보