我看到它suggested on a blog以下是使用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;
}
}
这两种方法都有明显的问题,是否有推荐的方法来实现这种查找?
答案 0 :(得分:23)
Guava对于构建查找地图非常方便。
更新:以下是使用{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);
}
}
答案 1 :(得分:19)
虽然它具有更高的开销,但静态映射很好,因为它提供code
的恒定时间查找。您的实现的查找时间随着枚举中的元素数量线性增加。对于小型枚举,这根本不会有显着贡献。
两个实现的一个问题(并且可以说,通常使用Java枚举)是Status
可以承担的隐藏的额外价值:null
。根据业务逻辑的规则,当查找“失败”时,返回实际的枚举值或抛出Exception
可能是有意义的。
答案 2 :(得分:8)
这是一个可能更快的替代方案:
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;
}
}
当然,如果您希望以后能够添加更多常量,那么这是不可维护的。
答案 3 :(得分:5)
显然,地图将提供恒定的时间查找,而循环则不会。在一个值很少的典型枚举中,我没有看到遍历查找的问题。
答案 4 :(得分:3)
这是一个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");
}
}
答案 5 :(得分:0)
两种方式都完全有效。他们在技术上具有相同的Big-Oh运行时间。
但是,如果首先将所有值保存到Map,则每次要执行查找时都会节省迭代整个集合所需的时间。所以,我认为静态地图和初始化程序是一种更好的方法。
答案 6 :(得分:0)
在Java 8中,我只是将以下工厂方法添加到您的枚举中,并跳过查找映射。
public static Optional<Status> of(int value) {
return Arrays.stream(values()).filter(v -> value == v.getCode()).findFirst();
}
答案 7 :(得分:0)
此线程中的所有其他答案都使用静态方法。我认为我们应该提供一个使用实例方法的示例作为替代:
public class Main {
public static void main(String[] args) {
for (Alphabet item : Alphabet.values()) {
System.out.println("GET: " + item.getValue() );
System.out.println("REV: " + item.reverseLookup(item.getValue()) );
}
}
}
enum Alphabet {
X(24) {
public String reverseLookup(int o) {
return "X";
}
},
Y(25) {
public String reverseLookup(int o) {
return "Y";
}
},
Z(26) {
public String reverseLookup(int o) {
return "Z";
}
};
abstract String reverseLookup(int o);
private final int value;
Alphabet(final int newValue) {
value = newValue;
}
public int getValue() { return value; }
}
答案 8 :(得分:0)
@AllArgsConstructor
@Getter
public enum MyEnum {
A(0),
B(1),
C(2);
private static final Map<Integer, MyEnum> LOOKUP =
Arrays.stream(MyEnum.values()).collect(Collectors.toMap(MyEnum::getStatus, Function.identity()));
private final int status;
@Nullable
public static MyEnum fromStatus(int status) {
return LOOKUP.get(status);
}
}