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

use*_*155 38 java enums

假设我有一个包含100个值的枚举.为简单起见,请参考以下示例:

public enum code
{
    CODE_1("string1"),
    CODE_2("string2"),
    CODE_3("string3"),
    CODE_4("string4"),
    ...
}
Run Code Online (Sandbox Code Playgroud)

我想创建一个公共方法与已知的格式字符串(如"字符串1","字符串2" ......)转换中选取适当的枚举值CODE_1,CODE_2 ......这通常是通过遍历所有的值来实现的,如果找到匹配项,返回该枚举值.(详情可在此问题中找到.)

但是,我关注的是对所有值进行reguraly循环.这可能是一个巨大的瓶颈吗?如果不是100个元素,那么有1000个?

作为我自己的练习,我尝试使用静态地图优化此查找,这可以确保给定任何字符串的O(1)查找时间.我喜欢这个额外的噱头,但我只想在我的代码中包含它,如果它确实是必要的.使用迭代方法和map方法有什么想法和发现?

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

Boh*_*ian 41

You want a Map<String, Code>, but how to populate it neatly? Enums don't allow you to initialize a static fields before the enum instances are initialized, but there's a neat little trick, called the Initialization-on-demand holder idiom, that makes using a statically initialized map needed for this functionality easy to implement:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

This works because the class loader initializes inner static classes before initializing the enum class, so the map is assigned ready to load during enum instance initialization.

没有循环.没有特殊的代码来加载地图(在构造函数中完成).最小的代码.

  • 所以你说我在上面的代码中填充它的方式不是"整洁"?你能解释为什么它不"整洁"吗?您还说"枚举不允许您在枚举实例初始化之前初始化静态字段",但我不认为我这样做.我在enum实例后填充地图? (2认同)
  • @ user1884155你的代码不整齐,因为它有很多 - 如果它 - 额外的方法,更多的行.这个程序只使用1行进行地图初始化(或者如果计算`class Holder`行则使用2行)并且只加载1行来加载它,没有定义额外的方法 - 这似乎是合理的称为"neater".至于不允许静态初始化的枚举,我的意思是你不能这样做:`private static Map <String,Code> CODE_MAP = new HashMap <>();` - 如果你这样做并尝试使用它,它将为null在枚举实例初始化期间,所以你不能像我从枚举的构造函数那样整齐地加载它. (2认同)
  • @greg你的链接articke的代码使用一个静态块来填充地图,这是普通静态地图所必需的,因为静态地图在初始化*所有枚举实例之后都没有初始化 - 使用构造函数自我加载太晚了每个实例,更整洁.由于一些原因,静态块被认为是不好的样式,一个是如果你将静态块移动到map声明之上,它将编译但是抛出一个NPE. (2认同)