通过为枚举添加静态持有人进行优化

时间:2018-10-14 07:05:21

标签: java optimization enums static-methods

我想知道添加一个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)。

此优化可以存在我所缺少的缺陷吗?

2 个答案:

答案 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()