Java 열거 형 역방향 조회 모범 사례
블로그 에서 다음이 getCode(int)
Java 열거 형에서를 사용하여 "역방향 조회"를 수행하는 합리적인 방법 이라고 제안한 것을 보았습니다 .
public enum Status {
WAITING(0),
READY(1),
SKIPPED(-1),
COMPLETED(5);
private static final Map<Integer,Status> lookup
= new HashMap<Integer,Status>();
static {
for(Status s : EnumSet.allOf(Status.class))
lookup.put(s.getCode(), s);
}
private int code;
private Status(int code) {
this.code = code;
}
public int getCode() { return code; }
public static Status get(int code) {
return lookup.get(code);
}
}
나에게 정적 맵과 정적 이니셜 라이저는 둘 다 나쁜 생각처럼 보이며 첫 번째 생각은 다음과 같이 조회를 코딩하는 것입니다.
public enum Status {
WAITING(0),
READY(1),
SKIPPED(-1),
COMPLETED(5);
private int code;
private Status(int code) {
this.code = code;
}
public int getCode() { return code; }
public static Status get(int code) {
for(Status s : values()) {
if(s.code == code) return s;
}
return null;
}
}
두 방법 중 하나에 명백한 문제가 있습니까? 이러한 종류의 조회를 구현하는 권장 방법이 있습니까?
Google Guava 의 Maps.uniqueIndex 는 조회 맵을 작성하는 데 매우 유용합니다.
업데이트 : 다음은 Maps.uniqueIndex
Java 8과 함께 사용하는 예입니다 .
public enum MyEnum {
A(0), B(1), C(2);
private static final Map<Integer, MyEnum> LOOKUP = Maps.uniqueIndex(
Arrays.asList(MyEnum.values()),
MyEnum::getStatus
);
private final int status;
MyEnum(int status) {
this.status = status;
}
public int getStatus() {
return status;
}
@Nullable
public static MyEnum fromStatus(int status) {
return LOOKUP.get(status);
}
}
오버 헤드가 높지만 정적 맵은 code
. 구현의 조회 시간은 열거 형의 요소 수에 따라 선형 적으로 증가합니다. 작은 열거 형의 경우 이것은 단순히 크게 기여하지 않습니다.
두 구현 (그리고 일반적으로 Java 열거 형)의 한 가지 문제는 a Status
가 취할 수 있는 숨겨진 추가 값이 있다는 것입니다 null
. 비즈니스 로직의 규칙에 따라 실제 열거 형 값을 반환하거나 Exception
조회가 "실패"할 때를 throw하는 것이 합리적 일 수 있습니다 .
더 빠른 대안이 있습니다.
public enum Status {
WAITING(0),
READY(1),
SKIPPED(-1),
COMPLETED(5);
private int code;
private Status(int code) {
this.code = code;
}
public int getCode() { return code; }
public static Status get(int code) {
switch(code) {
case 0: return WAITING;
case 1: return READY;
case -1: return SKIPPED;
case 5: return COMPLETED;
}
return null;
}
}
물론 나중에 더 많은 상수를 추가 할 수 있기를 원하면 실제로 유지 관리 할 수 없습니다.
분명히 맵은 일정한 시간 조회를 제공하지만 루프는 제공하지 않습니다. 값이 거의없는 일반적인 열거 형에서는 순회 조회에 문제가 없습니다.
다음은 Java 8 대안 (단위 테스트 포함)입니다.
// DictionarySupport.java :
import org.apache.commons.collections4.Factory;
import org.apache.commons.collections4.map.LazyMap;
import java.util.HashMap;
import java.util.Map;
public interface DictionarySupport<T extends Enum<T>> {
@SuppressWarnings("unchecked")
Map<Class<?>, Map<String, Object>> byCodeMap = LazyMap.lazyMap(new HashMap(), (Factory) HashMap::new);
@SuppressWarnings("unchecked")
Map<Class<?>, Map<Object, String>> byEnumMap = LazyMap.lazyMap(new HashMap(), (Factory) HashMap::new);
default void init(String code) {
byCodeMap.get(this.getClass()).put(code, this);
byEnumMap.get(this.getClass()).put(this, code) ;
}
static <T extends Enum<T>> T getByCode(Class<T> clazz, String code) {
clazz.getEnumConstants();
return (T) byCodeMap.get(clazz).get(code);
}
default <T extends Enum<T>> String getCode() {
return byEnumMap.get(this.getClass()).get(this);
}
}
// Dictionary 1:
public enum Dictionary1 implements DictionarySupport<Dictionary1> {
VALUE1("code1"),
VALUE2("code2");
private Dictionary1(String code) {
init(code);
}
}
// Dictionary 2:
public enum Dictionary2 implements DictionarySupport<Dictionary2> {
VALUE1("code1"),
VALUE2("code2");
private Dictionary2(String code) {
init(code);
}
}
// DictionarySupportTest.java:
import org.testng.annotations.Test;
import static org.fest.assertions.api.Assertions.assertThat;
public class DictionarySupportTest {
@Test
public void teetSlownikSupport() {
assertThat(getByCode(Dictionary1.class, "code1")).isEqualTo(Dictionary1.VALUE1);
assertThat(Dictionary1.VALUE1.getCode()).isEqualTo("code1");
assertThat(getByCode(Dictionary1.class, "code2")).isEqualTo(Dictionary1.VALUE2);
assertThat(Dictionary1.VALUE2.getCode()).isEqualTo("code2");
assertThat(getByCode(Dictionary2.class, "code1")).isEqualTo(Dictionary2.VALUE1);
assertThat(Dictionary2.VALUE1.getCode()).isEqualTo("code1");
assertThat(getByCode(Dictionary2.class, "code2")).isEqualTo(Dictionary2.VALUE2);
assertThat(Dictionary2.VALUE2.getCode()).isEqualTo("code2");
}
}
Java 8에서는 열거 형에 다음 팩토리 메소드를 추가하고 맵 조회를 건너 뜁니다.
public static Optional<Status> of(int value) {
return Arrays.stream(values()).filter(v -> value == v.getCode()).findFirst();
}
두 가지 방법 모두 완벽하게 유효합니다. 그리고 그들은 기술적으로 동일한 Big-Oh 실행 시간을 가지고 있습니다.
그러나 먼저 모든 값을 맵에 저장하면 조회를 수행 할 때마다 집합을 반복하는 데 걸리는 시간을 절약 할 수 있습니다. 그래서 저는 정적 맵과 이니셜 라이저가 약간 더 나은 방법이라고 생각합니다.
참고 URL : https://stackoverflow.com/questions/5316311/java-enum-reverse-look-up-best-practice
'Nice programing' 카테고리의 다른 글
이미지 src를 데이터 URL로 설정하면 즉시 사용할 수 있습니까? (0) | 2020.10.30 |
---|---|
app.config 파일과 XYZ.settings 파일의 차이점은 무엇입니까? (0) | 2020.10.30 |
JavaScript에서 "클로저"는 정확히 무엇을 의미합니까? (0) | 2020.10.30 |
LinearLayout 대 RelativeLayout (0) | 2020.10.30 |
수직으로 고정 된 위치 요소, 절대 수평으로 고정 (0) | 2020.10.30 |