从String转换为具有大量值的Java枚举

Convert from String to a Java enum with large amount of values

本问题已经有最佳答案,请猛点这里访问。

假设我有一个具有100个值的枚举。为了简单起见,请使用以下示例:

1
2
3
4
5
6
7
8
public enum code
{
    CODE_1("string1"),
    CODE_2("string2"),
    CODE_3("string3"),
    CODE_4("string4"),
    ...
}

我想创建一个公共方法来将具有已知格式(如"string1"、"string2"…)的字符串转换为适当的枚举值代码_1、代码_2…通常,这是通过对所有值进行迭代来完成的,如果找到匹配项,则返回该枚举值。(在这个问题中可以找到详细信息。)

然而,我关注的是所有价值观的规范循环。这可能是一个巨大的瓶颈吗?如果不是100个元素,而是1000个呢?

作为我自己的一个练习,我尝试使用静态映射来优化这个查找,这可以确保给定任何字符串的O(1)查找时间。我喜欢这个额外的花招,但我只想在我的代码中包含它,如果真的有必要的话。您对使用迭代方法和映射方法有什么想法和发现?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public enum Code
{
    ...
    //enum values
    ...


    //The string-to-Code map
    private static final Map<String,Code> CODE_MAP = populateMap();

    private static Map<String,Code> populateMap()
    {
        Map<String,Code> map = new HashMap<String,Code>();

        for(Code c : Code.values())
        {
            map.put(c.getCode(), c);
        }

        return map;
    }


    private String code;

    private Code(String code)
    {
        this.code = code;
    }

    public String getCode()
    {
        return this.code;
    }

    public Code convertFromString(String code)
    {
        //assume that the given string is actually a key value in the map

        return (Code) CODE_MAP.get(code);
    }
}


你想要一个Map,但是如何整齐地填充它呢?枚举不允许您在初始化枚举实例之前初始化静态字段,但有一个简单的小技巧,称为"按需初始化保持器"习语,它使使用此功能所需的静态初始化映射变得容易实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public enum Code {
    CODE_1("string1"),
    CODE_2("string2"),
    CODE_3("string3"),
    // etc
    ;

    private static class Holder {
        static Map<String, Code> CODE_MAP = new HashMap<>();
    }

    private final String code;

    private Code(String code) {
        this.code = code;
        Holder.CODE_MAP.put(code, this);
    }

    public String getCode() {
        return this.code;
    }

    public Code convertFromString(String code) {
        return CODE_MAP.get(code);
    }
}

这是因为类加载器在初始化枚举类之前初始化内部静态类,所以在枚举实例初始化期间,映射被分配为随时可以加载。

没有循环。没有用于加载映射的特殊代码(在构造函数中完成)。最小代码。


如果字符串代码值是已知且一致的格式,则可以避免使用映射及其消耗的内存,并动态构造代码枚举查找值:

1
2
3
  public static Code convertFromString(String code) {
    return valueOf("CODE_" + code.substring("string".length()));
  }


您提供的解决方案是正确的实现。

因为您只需要公开一个方法,而且它更具可读性。

使用Map而不是iterating it manually,总是很好的。

正如你提到的,复杂性是O(1)

+1 to your question,因为它给了a cleaner approach to use enum in some usecases


地图是很好的选择:更清晰的代码和O(1)。如果你使用for-loop,那么你得到的最好的是O(n)


好吧,映射解决方案的替代方案是一个巨大的switch语句(可以自动生成)或二进制搜索包含字符串的数组。我认为两者都不会在很大程度上超过HashMap的性能,尽管如果这真的很重要,那么通过基准测试您可能是最好的选择。

还有一件事没有被提到,那就是如果字符串具有某个枚举成员的确切名称,那么让我们将它转换为枚举值。如果这在您的案例中是完全可能的(只看您的示例,我看不到code.code_1如何不容易重命名code.string1等),我建议使用它,因为它根本不需要额外的编码,因此是最容易理解的。