Як я можу знайти перерахунок Java з його значення String?


164

Я хотів би знайти enum з його рядкового значення (або, можливо, будь-якого іншого значення). Я спробував наступний код, але він не дозволяє статичні в ініціалізаторах. Чи є простий спосіб?

public enum Verbosity {

    BRIEF, NORMAL, FULL;

    private static Map<String, Verbosity> stringMap = new HashMap<String, Verbosity>();

    private Verbosity() {
        stringMap.put(this.toString(), this);
    }

    public static Verbosity getVerbosity(String key) {
        return stringMap.get(key);
    }
};

IIRC, це дає NPE, оскільки статична ініціалізація проводиться зверху вниз (тобто константи перерахунку вгорі будуються до того, як вона опуститься до stringMapініціалізації). Звичайним рішенням є використання вкладеного класу.
Том Хотін - тайклін

Дякую всім за таку швидку реакцію. (FWIW Я не вважав Sun Javadocs дуже корисним для цієї проблеми).
peter.murray.rust

Це справді мовне питання, ніж питання бібліотеки. Однак, я думаю, що документи API читаються більше, ніж JLS (хоча, можливо, не мовні дизайнери), тому такі речі, мабуть, повинні мати більше уваги в документах java.lang.
Том Хотін - тайклін

Відповіді:


241

Використовуйте valueOfметод, який автоматично створюється для кожного Enum.

Verbosity.valueOf("BRIEF") == Verbosity.BRIEF

Для довільних значень почніть з:

public static Verbosity findByAbbr(String abbr){
    for(Verbosity v : values()){
        if( v.abbr().equals(abbr)){
            return v;
        }
    }
    return null;
}

Перейдіть пізніше до реалізації Карти, лише якщо ваш профайлер повідомить вам.

Я знаю, що це ітерація над усіма значеннями, але, маючи лише 3 значення перерахунку, навряд чи варто докладати будь-яких інших зусиль, насправді, якщо у вас є багато значень, я б не заважав Карту, це буде досить швидко.


дякую - і я можу використовувати перетворення справ, якщо знаю, що значення все ще відрізняються
peter.murray.rust

Ви можете отримати доступ до члена класу 'abbr', тому замість "v.abbr ()" ви можете використовувати "v.abbr.equals ...".
Amio.io

7
Крім того, для довільних значень (другий випадок) розглянути можливість кидання IllegalArgumentExceptionзамість повернення нуля ( тобто коли не знайдено відповідності) - так, як це Enum.valueOf(..)робить JDK .
Priidu Neemre

135

Ти поруч. Для довільних значень спробуйте щось подібне:

public enum Day { 

    MONDAY("M"), TUESDAY("T"), WEDNESDAY("W"),
    THURSDAY("R"), FRIDAY("F"), SATURDAY("Sa"), SUNDAY("Su"), ;

    private final String abbreviation;

    // Reverse-lookup map for getting a day from an abbreviation
    private static final Map<String, Day> lookup = new HashMap<String, Day>();

    static {
        for (Day d : Day.values()) {
            lookup.put(d.getAbbreviation(), d);
        }
    }

    private Day(String abbreviation) {
        this.abbreviation = abbreviation;
    }

    public String getAbbreviation() {
        return abbreviation;
    }

    public static Day get(String abbreviation) {
        return lookup.get(abbreviation);
    }
}

4
Через проблеми з завантажувачем класу цей метод не буде надійним. Я не рекомендую його, і я бачив, що він виходить з ладу регулярно, оскільки карта пошуку не готова до доступу. Потрібно поставити "пошук" поза enum або в інший клас, щоб він завантажився першим.
Адам Гент

7
@Adam Gent: Внизу є посилання на JLS, де ця точна конструкція є прикладом. У ній сказано, що статична ініціалізація відбувається зверху вниз: docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#d5e12267
Селена

3
Так, сучасна java, як java 8, зафіксувала модель пам'яті. Це, як говорять, агенти гарячої заміни, такі як jrebel, все-таки заплутається, якщо вбудувати ініціалізацію через кругові посилання.
Адам Гент

@Adam Gent: Хммм. Я щодня дізнаюся щось нове. [Тихо ковзаю, щоб рефакторировать мої перерахунки за допомогою синглонів завантаження ...] Вибачте, якщо це дурне запитання, але це здебільшого проблема статичної ініціалізації?
Селена

Я ніколи не бачив, щоб проблеми з статичним блоком коду не ініціалізувалися, але я використовував Java 8 з моменту свого запуску в Java в 2015 році. Я просто зробив невеликий орієнтир, використовуючи JMH 1,22 і використовуючи a HashMap<>для зберігання перерахунків, які виявилися більш ніж на 40% швидше, ніж нижня петля.
cbaldan

21

з Java 8 ви можете досягти цього:

public static Verbosity findByAbbr(final String abbr){
    return Arrays.stream(values()).filter(value -> value.abbr().equals(abbr)).findFirst().orElse(null);
}

Я б кинув виняток, а не повертався null. Але це також залежить від випадку використання
Олександр

12

@ Відповідь Лайла досить небезпечна, і я бачив, що вона не спрацьовує особливо, якщо зробити перерахунок статичним внутрішнім класом. Натомість я використав щось подібне, яке завантажить карти BootstrapSingleton перед перерахунками.

Відредагувати це більше не повинно бути проблемою із сучасними JVM (JVM 1.6 або новішою), але я думаю, що з JRebel все ще є проблеми, але я не мав шансу повторно перевірити це .

Завантажте мене спочатку:

   public final class BootstrapSingleton {

        // Reverse-lookup map for getting a day from an abbreviation
        public static final Map<String, Day> lookup = new HashMap<String, Day>();
   }

Тепер завантажте його в конструктор enum:

   public enum Day { 
        MONDAY("M"), TUESDAY("T"), WEDNESDAY("W"),
        THURSDAY("R"), FRIDAY("F"), SATURDAY("Sa"), SUNDAY("Su"), ;

        private final String abbreviation;

        private Day(String abbreviation) {
            this.abbreviation = abbreviation;
            BootstrapSingleton.lookup.put(abbreviation, this);
        }

        public String getAbbreviation() {
            return abbreviation;
        }

        public static Day get(String abbreviation) {
            return lookup.get(abbreviation);
        }
    }

Якщо у вас є внутрішній перерахунок, ви можете просто визначити Map над визначенням enum, який (теоретично) повинен завантажуватися раніше.


3
Необхідно визначити "небезпечне" і чому важлива реалізація внутрішнього класу. Це скоріше питання статичного визначення «зверху вниз», але є робочий код у іншій відповідній відповіді із посиланням на це запитання, який використовує статичну карту в самому екземплярі Enum методом «get» від визначеного користувачем значення до Тип Енума. stackoverflow.com/questions/604424/lookup-enum-by-string-value
Darrell Teague

2
@DarrellTeague початковий випуск був пов'язаний зі старими JVM (попередньо 1.6) або іншими JVM (IBM) або замінами гарячого коду (JRebel). Питання не повинно траплятися в сучасних СП, тому я можу просто видалити свою відповідь.
Адам Гент

Добре зауважити, що це дійсно можливо завдяки індивідуальним реалізаціям компілятора та оптимізаціям виконання ... замовлення може бути повторно виконане, що призведе до помилки. Однак це могло б порушити специфікацію.
Darrell Teague

7

І ви не можете використовувати valueOf () ?

Редагувати: Btw, ніщо не заважає тобі використовувати static {} в enum.


Або синтетичний valueOfдля класу enum, тому вам не потрібно вказувати клас enum Class.
Том Хотін - тайклін

Звичайно, у нього просто не було запису на javadoc, тому з цим було важко зв’язатись.
Фредрік

1
Ви можете використовувати valueOf (), але ім'я повинно відповідати ідентичному встановленому в декларації enum. Будь-який із наведених вище двох способів пошуку може бути модифікований для використання .equalsIgnoringCase () і має трохи більше надійності до помилок.

@leonardo Правда. Якщо це додає надійності або просто відмовостійкість є дискусійною. У більшості випадків я б сказав, що це останнє, і в цьому випадку краще обробити його в іншому місці і все ж використовувати значення Enum'sOf ().
Фредрік

4

У випадку, якщо він допомагає іншим, варіант, який я віддаю перевагу, який не вказаний тут, використовує функції Карт Guava :

public enum Vebosity {
    BRIEF("BRIEF"),
    NORMAL("NORMAL"),
    FULL("FULL");

    private String value;
    private Verbosity(final String value) {
        this.value = value;
    }

    public String getValue() {
        return this.value;
    }

    private static ImmutableMap<String, Verbosity> reverseLookup = 
            Maps.uniqueIndex(Arrays.asList(Verbosity.values()), Verbosity::getValue);

    public static Verbosity fromString(final String id) {
        return reverseLookup.getOrDefault(id, NORMAL);
    }
}

За умовчанням, яким ви можете скористатися null, ви можете throw IllegalArgumentExceptionабо fromStringможете повернути Optional, незалежно від поведінки.


3

оскільки java 8 ви можете ініціалізувати карту в один рядок і без статичного блоку

private static Map<String, Verbosity> stringMap = Arrays.stream(values())
                 .collect(Collectors.toMap(Enum::toString, Function.identity()));

+1 Відмінне використання потоків і дуже стисле, не приносячи шкоди читанню! Я також раніше не бачив цього використання Function.identity(), я вважаю його набагато більш текстуально привабливим, ніж e -> e.
Мацу Q.

0

Можливо, погляньте на це. Це працює для мене. Метою цього є пошук 'ЧЕРВЕНОГО' за допомогою '/ red_color'. Оголошення a static mapта завантаження enums в нього лише один раз принесуть певні переваги продуктивності, якщо enums багато.

public class Mapper {

public enum Maps {

    COLOR_RED("/red_color", "RED");

    private final String code;
    private final String description;
    private static Map<String, String> mMap;

    private Maps(String code, String description) {
        this.code = code;
        this.description = description;
    }

    public String getCode() {
        return name();
    }

    public String getDescription() {
        return description;
    }

    public String getName() {
        return name();
    }

    public static String getColorName(String uri) {
        if (mMap == null) {
            initializeMapping();
        }
        if (mMap.containsKey(uri)) {
            return mMap.get(uri);
        }
        return null;
    }

    private static void initializeMapping() {
        mMap = new HashMap<String, String>();
        for (Maps s : Maps.values()) {
            mMap.put(s.code, s.description);
        }
    }
}
}

Будь ласка, покладіть свої думки.


-1

Ви можете визначити свій Enum наступним кодом:

public enum Verbosity 
{
   BRIEF, NORMAL, FULL, ACTION_NOT_VALID;
   private int value;

   public int getValue()
   {
     return this.value;
   } 

   public static final Verbosity getVerbosityByValue(int value)
   {
     for(Verbosity verbosity : Verbosity.values())
     {
        if(verbosity.getValue() == value)
            return verbosity ;
     }

     return ACTION_NOT_VALID;
   }

   @Override
   public String toString()
   {
      return ((Integer)this.getValue()).toString();
   }
};

Для отримання більш детальних роз'яснень див. Наступне посилання


-1

Якщо ви хочете значення за замовчуванням і не хочете будувати карти пошуку, ви можете створити статичний метод, щоб впоратися з цим. Цей приклад також обробляє пошуки, де очікуване ім'я починалося б з числа.

    public static final Verbosity lookup(String name) {
        return lookup(name, null);
    }

    public static final Verbosity lookup(String name, Verbosity dflt) {
        if (StringUtils.isBlank(name)) {
            return dflt;
        }
        if (name.matches("^\\d.*")) {
            name = "_"+name;
        }
        try {
            return Verbosity.valueOf(name);
        } catch (IllegalArgumentException e) {
            return dflt;
        }
    }

Якщо вам це потрібно для другорядного значення, ви просто спочатку побудуєте карту пошуку, як у деяких інших відповідях.


-1
public enum EnumRole {

ROLE_ANONYMOUS_USER_ROLE ("anonymous user role"),
ROLE_INTERNAL ("internal role");

private String roleName;

public String getRoleName() {
    return roleName;
}

EnumRole(String roleName) {
    this.roleName = roleName;
}

public static final EnumRole getByValue(String value){
    return Arrays.stream(EnumRole.values()).filter(enumRole -> enumRole.roleName.equals(value)).findFirst().orElse(ROLE_ANONYMOUS_USER_ROLE);
}

public static void main(String[] args) {
    System.out.println(getByValue("internal role").roleName);
}

}


-2

Ви можете використовувати Enum::valueOf()функцію, запропоновану вище Гаретом Девісом та Бредом Мейсом, але переконайтеся, що ви обробляєте те, IllegalArgumentExceptionщо було б кинуто, якщо рядок, що використовується, не присутній у перерахунку.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.