Java enum反向查找最佳实践

我在博客上看到,在Java枚举中使用getCode(int)进行“反向查找”是一种合理的方法:

 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; } } 

这两种方法是否存在明显的问题?是否有推荐的方法来实现这种查找?

虽然它有更高的开销,但静态映射是很好的,因为它提供了code恒定时间查找。 您的实现的查找时间随着枚举中元素的数量线性增加。 对于小枚举,这根本不会有太大贡献。

这两个实现(以及可以说一般的Java枚举)都有一个问题,那就是Status可以有一个额外的隐藏值: null 。 根据业务逻辑的规则,当查找“失败”时返回一个实际的枚举值或抛出一个Exception有意义的。

来自Google Guava的 Maps.uniqueIndex对于构build查找地图非常方便。

更新:这是一个使用Java 8的Maps.uniqueIndex的例子:

 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); } } 

这是一个可能会更快的替代scheme:

 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; } } 

当然,如果你希望能够在以后添加更多的常量,这是不可维护的。

地图显然会提供恒定的时间查询,而循环不会。 在一个典型的值很less的枚举中,我没有看到遍历查找的问题。

两种方式都是完全有效的。 而且他们在技术上也有相同的Big-Oh运行时间。

但是,如果首先将所有值保存到Map中,则每次要执行查找时,都可以节省迭代遍历整个集合的时间。 所以,我认为静态映射和初始化是一个稍微好一些的方法。

这是一个Java 8的替代(与unit testing):

 // 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(); }