Подтвердить что ты не робот

Преобразование из String в перечисление Java с большим количеством значений

Скажем, у меня есть перечисление со 100 значениями. Для простоты сделайте следующий пример:

public enum code
{
    CODE_1("string1"),
    CODE_2("string2"),
    CODE_3("string3"),
    CODE_4("string4"),
    ...
}

Я хочу создать общедоступный метод для преобразования строк с известным форматом (например, "string1", "string2"...) в соответствующее значение перечисления CODE_1, CODE_2... Обычно это выполняется путем итерации по всем значениям, и если совпадение найдено, верните это значение перечисления. (Подробности можно найти в этом вопросе.)

Тем не менее, я обеспокоен циклом reguraly по всем значениям. Может ли это быть огромным узким местом? Что, если вместо 100 элементов было 1000?

В качестве упражнения для себя я попытался оптимизировать этот поиск со статической картой, которая может гарантировать время поиска O (1), заданное любой строкой. Мне нравится этот дополнительный трюк, но я хочу включить его в свой код, если это действительно необходимо. Каковы ваши мысли и выводы об использовании метода итерации против метода карты?

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);
    }
}
4b9b3361

Ответ 1

Вы хотите Map<String, Code>, но как аккумулировать его? Перечисления не позволяют инициализировать статические поля до инициализации экземпляров перечисления, но есть аккуратный маленький трюк, называемый Инициализация по требованию владельца идиомы, который позволяет использовать статически инициализированную карту, необходимую для этой функциональности, легко реализовать:

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);
    }
}

Это работает, потому что загрузчик классов инициализирует внутренние статические классы перед инициализацией класса enum, поэтому карта назначается готовой к загрузке во время инициализации экземпляра enum.

Нет циклов. Нет специального кода для загрузки карты (сделано в конструкторе). Минимальный код.

Ответ 2

Карта - хороший вариант: более чистый код и O(1). Если вы используете for-loop, то лучшее, что вы получите, это O(n)

Ответ 3

Ваше предоставленное решение - это правильная реализация.

Как вам придется выставлять только один метод, и это более читаемо.

И всегда полезно использовать Map вместо iterating it manually.

А также, как вы сказали, сложность O(1).

+1 to your question, так как он дает a cleaner approach to use enum in some usecases.

Ответ 4

Если значение вашего строкового кода является известным и согласованным форматом, вы можете избежать использования Карты и памяти, которую она потребляет, и построить значение поиска перечисления CODE на лету:

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

Ответ 5

Ну, альтернативами решения-карты будет гигантский оператор switch (может быть автоматически сгенерирован) или двоичный поиск массива, содержащего строки. Я не думаю, что слишком сильно пойдет на производительность HashMap, хотя, если это действительно имеет значение, вы, вероятно, лучше всего сравниваете.

Одна вещь, о которой не упоминалось, - это то, как Enum.valueOf() позволяет превратить String в значение enum, если оно имеет точное имя одного из членов перечисления. Если это вообще возможно в вашем случае (глядя только на ваш пример, я не вижу, как Code.CODE_1 нельзя было бы легко переименовать Code.string1 и т.д.), Я бы предложил использовать его, поскольку он не требует дополнительного кодирования и, следовательно, будет наиболее понятным.