Enum에서 특정 문자열을 기준으로 해당하는 Enum 값을 찾는 메서드를 자주 사용합니다. 예를 들어, find 또는findByValue
와 같은 메서드를 통해 문자열 값과 Enum 값을 매칭합니다. Stream API를 사용하여 Enum 값을 찾는 방식은 간단하고 직관적이지만, 반복적으로 호출될 경우 성능 상의 문제가 발생할 수 있습니다. 이에 Jay 님이 작성한 Enum 최적화 방법을 참고하여, HashMap
을 사용하여 값을 캐싱하고 빠르게 찾는 방법을 기록하고자 합니다
Enum에서 특정 값(예: 문자열)을 기준으로 Enum 인스턴스를 찾는 방법 3가지 예시를 보여드리겠습니다. 각각의 방법은 Stream과 HashMap을 활용한 구현으로 구성됩니다.
@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);
}
}
@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);
}
}
@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);
}
}
위 코드는 다음과 같은 방식으로 동작합니다:
불변 Map
생성
descriptions
는 description
을 키로, Status
를 값으로 하는 불변 Map
입니다. 이를 생성하기 위해 Collectors.toMap
을 사용하고, 외부에서 수정할 수 없도록 Collections.unmodifiableMap
으로 감쌉니다.
Collectors.toMap
사용
Status::getDescription
으로, 각 Status
의 description
값을 키로 사용합니다.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.stream
과 Streams.of
방식은 속도 차이가 미미했으나, HashMap
을 사용한 방식은 두 방법에 비해 약 10배 이상 빠른 결과를 보였습니다. 다음 프로젝트 부터 Enum 사용 시 HashMap 사용을 고려해봐도 좋을 거 같습니다!