我想知道添加一个Enum
静态持有人是否总是比“ get” Enum方法上的值迭代更好的实现(或类似的用于获取特定Enum值的方法)。
例如对于Spring HttpStatus当前实现:
HttpStatus(int value, String reasonPhrase) {
this.value = value;
this.reasonPhrase = reasonPhrase;
}
public static HttpStatus valueOf(int statusCode) {
for (HttpStatus status : values()) {
if (status.value == statusCode) {
return status;
}
}
throw new IllegalArgumentException("No matching constant for [" + statusCode + "]");
}
可以通过以下方式进行优化:
private static class Holder {
private static Map<Integer, HttpStatus> enumMap = new HashMap<>();
}
HttpStatus(int value, String reasonPhrase) {
this.value = value;
this.reasonPhrase = reasonPhrase;
Holder.enumMap.put(value, this);
}
public static HttpStatus valueOf(int statusCode) {
return Holder.enumMap.computeIfAbsent(statusCode, statusCode -> {
throw new IllegalArgumentException("No matching constant for [" + statusCode + "]"); });
}
代码优化:
循环版本具有线性时间复杂度(每次请求获取值),而使用HashMap的版本具有时间复杂度O(1)。
此优化可以存在我所缺少的缺陷吗?
答案 0 :(得分:3)
要记住的一件事是,一旦初始化,静态映射将无限期保存在内存中;循环方法仅在循环迭代期间保存通过调用values()
创建的数组。
但是,值得指出的是,使用Holder类没有任何好处:因为要在构造函数中添加地图,所以调用构造函数时,将立即初始化Holder类。
同样,您也可以使用普通的旧静态(最终)字段。
还值得考虑的是,这种方法必然要求地图是可变的。您可能不打算进行更改,但是值得在防御上进行更改,以使您不能。
相反,您可以直接在字段上进行初始化:
static final Map<Integer, HttpStatus> map =
Collections.unmodifiableMap(
Stream.of(HttpStatus.values())
.collect(toMap(s -> s.value, s -> s)));
(或使用番石榴ImmutableMap
之类的东西)
关于直接初始化地图的方法的另一点是,它不会添加到构造函数中的地图中-这意味着您实际上不必将其放入枚举类本身。这样,您便可以灵活地在无法更改代码的枚举中使用它,和/或仅将地图添加到发现有性能需求的地方。
答案 1 :(得分:0)
我很早以前就检查过这种方法,因此决定没有好处使用Map
而不是手动使用循环值。
O(1)
搜索的地图确实比O(n)
手动循环好,枚举应具有1000个以上的常数(这是实验数字)。但是根据我的经验,我列举了最多包含200个以上常量的国家。我总是使用手动循环,而不用担心性能。许多序列化框架(例如将Jackson枚举转换为json的Jackson)都使用Enum.valueOf()
。