Як я можу знайти перерахунок 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.