Java: Перевірте, чи містить enum заданий рядок?


169

Ось моя проблема - я шукаю (якщо він навіть існує) еквівалент перерахунку ArrayList.contains();.

Ось зразок моєї проблеми з кодом:

enum choices {a1, a2, b1, b2};

if(choices.???(a1)}{
//do this
} 

Тепер я розумію , що ArrayListз Stringsбуде кращий маршрут тут , але я повинен виконати моє вміст перерахувань через комутатор / випадок в іншому місці. Звідси моя проблема.

Якщо припустити, що щось подібне не існує, як я міг би робити це?


Switch / case з рядками реалізований починаючи з Java 7
AndreyP

Відповіді:


205

Це слід зробити:

public static boolean contains(String test) {

    for (Choice c : Choice.values()) {
        if (c.name().equals(test)) {
            return true;
        }
    }

    return false;
}

Це означає, що вам не доведеться турбуватися про додавання додаткових значень перерахунків пізніше, вони перевіряються.

Редагувати: Якщо перерахунок дуже великий, ви можете вставити значення в HashSet:

public static HashSet<String> getEnums() {

  HashSet<String> values = new HashSet<String>();

  for (Choice c : Choice.values()) {
      values.add(c.name());
  }

  return values;
}

Тоді ви можете просто зробити: values.contains("your string")що повертає істинне чи хибне.


12
це дуже поганий імпл .: Choice.valueOf (тест) - те, що ви хочете (з / спробувати / зловити)
bestsss

17
bestsss, це, очевидно, найбільш підходяще рішення. Закидання винятку для впровадження типу методу існує () є поганою практикою. Хоча ви можете подумати, що ваша реалізація більш ефективна завдяки тому, що вона не виглядає O (n), вона знаходиться в базовій основі, яка не видно. Також використання try {} catch додає накладні витрати. Плюс, це просто не красиво.
jluzwick

25
@Jred, безумовно valueOf. Просто зловіть своє виняток і поверніть хибне. Тим, хто говорить інакше, якщо ви подивитесь на реалізацію, вона вже використовує Map, і розробники JDK мають набагато кращі шанси оптимізувати це. API викидає виняток, що є дискусійною практикою (замість повернення нуля), але коли ви маєте справу з API, який викидає виняток, переходьте до цього, не винаходити колесо.
Yishai

2
@jluzwick over / catch overhead - це проста інструкція зі стрибків, якщо вона не взята, не використовується виняток у блоці вилову. Побоюватися спроби / зловити причину втрати продуктивності - це погана практика.
bestsss

22
містить () слід віддати перевагу вартості valueOf () за винятком. Чому? Тому що "винятки, як випливає з їх назви, повинні використовуватися лише у виняткових умовах; вони ніколи не повинні використовуватися для звичайних контрольних потоків" (Джошуа Блох, "Ефективна Java").
james.garriss

226

Використовуйте замість цього ліб Apache commons lang3

 EnumUtils.isValidEnum(MyEnum.class, myValue)

33
Зауважте для зацікавлених, основна реалізація, яку вони застосовували, - це просто рішення try / catch (@since 3.0 @version $ Id: EnumUtils.java 1199894 2011-11-09 17: 53: 59Z ggregory $).
Джонатан Гаврич

1
Робить мій код простішим, тому мені байдуже, якщо він використовує виключення для контролю потоку (вони справді не повинні) ... Було б добре, якби вони змінили це.
jpangamarca


1
Чи містить гуава також такий розчин?
Cypress Frankenfeld

50

Можна використовувати Enum.valueOf()

enum Choices{A1, A2, B1, B2};

public class MainClass {
  public static void main(String args[]) {
    Choices day;

    try {
       day = Choices.valueOf("A1");
       //yes
    } catch (IllegalArgumentException ex) {  
        //nope
  }
}

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


8
Я не думаю, що виняток є найкращим вибором у такому випадку.
GokcenG

6
Спробуйте і ловити слід в крайньому випадку. Спробуйте і зловити дорого
Ісус Дімрікс

2
покладатися на винятки під час виконання ділової логіки, окрім дорогої, не настільки читабельна. що стосується перевірених винятків, то це інакше, оскільки вони є частиною бізнесу.
Luís Soares

2
Це також заважає комусь увімкнути широкий перерв на винятки, щоб викинути фактичні виняткові випадки, які повторно переглядаються. (або принаймні робить це дуже прикро). Використовуйте винятки у виняткових випадках.
Микола Кондратьєв

4
EnumUtils.isValidEnum(MyEnum.class, myValue)використовує подібну логіку, і IMO має сенс додати цілу бібліотеку для цього тривіального завдання
Vikramjit Roy

37

Якщо ви використовуєте Java 1.8, ви можете вибрати Stream + Lambda, щоб здійснити це:

public enum Period {
    DAILY, WEEKLY
};

//This is recommended
Arrays.stream(Period.values()).anyMatch((t) -> t.name().equals("DAILY1"));
//May throw java.lang.IllegalArgumentException
Arrays.stream(Period.values()).anyMatch(Period.valueOf("DAILY")::equals);

19

Навіть краще:

enum choices {
   a1, a2, b1, b2;

  public static boolean contains(String s)
  {
      for(choices choice:values())
           if (choice.name().equals(s)) 
              return true;
      return false;
  } 

};

Дякую за оптимістичне рішення
Parth Patel

19

Гуавас Енумс може стати вашим другом

Наприклад, наприклад:

enum MyData {
    ONE,
    TWO
}

@Test
public void test() {

    if (!Enums.getIfPresent(MyData.class, "THREE").isPresent()) {
        System.out.println("THREE is not here");
    }
}

11

Ви можете спочатку перетворити enum у List, а потім скористатися методом list list

enum Choices{A1, A2, B1, B2};

List choices = Arrays.asList(Choices.values());

//compare with enum value 
if(choices.contains(Choices.A1)){
   //do something
}

//compare with String value
if(choices.contains(Choices.valueOf("A1"))){
   //do something
}

Це має бути прийнятою відповіддю. Перетворення до списку - це найчистіший спосіб зробити це. Це заощаджує всю дискусію про "правильне використання винятків" на Java в інших відповідях тут.
Мануель

Киньте IllegalArgumentException, якщо значення не існує.
mkyong

10

Тут згадується пара бібліотек, але я сумую за тією, яку я насправді шукав: Весна!

Існує ObjectUtils # containsConstant , який чутливий до регістру за замовчуванням, але може бути суворим , якщо ви хочете. Він використовується так:

if(ObjectUtils.containsConstant(Choices.values(), "SOME_CHOISE", true)){
// do stuff
}

Примітка. Тут я використовував перевантажений метод, щоб продемонструвати, як використовувати перевірку з урахуванням регістру. Ви можете опустити булеву, щоб мати нечутливу поведінку.

Будьте обережні з великими перерахунками, оскільки вони не використовують реалізацію Map, як це роблять деякі ...

Як бонус, він також надає нечутливий до регістру варіант valueOf: ObjectUtils # caseInsensitiveValueOf


9

Трохи припущень:
1) Немає спроб / лову, оскільки це надзвичайний контроль потоку
2) метод "містить" повинен бути швидким, оскільки він зазвичай працює кілька разів.
3) Місце не обмежене (звичайне для звичайних рішень)

import java.util.HashSet;
import java.util.Set;

enum Choices {
    a1, a2, b1, b2;

    private static Set<String> _values = new HashSet<>();

    // O(n) - runs once
    static{
        for (Choices choice : Choices.values()) {
            _values.add(choice.name());
        }
    }

    // O(1) - runs several times
    public static boolean contains(String value){
        return _values.contains(value);
    }
}

7

Ви можете використовувати це

YourEnum {A1, A2, B1, B2}

boolean contains(String str){ 
    return Sets.newHashSet(YourEnum.values()).contains(str);
}                                  

Оновлення, запропоноване @ wightwulf1944, включено, щоб зробити рішення більш ефективним.


4
Ця реалізація неефективна. Це повторює значення enum, щоб створити новий набір, потім створює потік, який повторюється через отриманий набір. Це означає, що кожен раз, коли функція викликається, створюється новий Set and Stream, а використання stream()набору означає, що ви повторюєте кожен елемент у наборі, замість того, щоб скористатися базовим хештелем, який буде швидшим. Щоб покращити це, найкраще кешувати створений набір і використовувати його contains()метод замість цього. Якщо вам потрібно отримати потік, використовуйте Arrays.stream()замість цього.
Subaru Tashiro

3

Я не думаю, що існує, але ви можете зробити щось подібне:

enum choices {a1, a2, b1, b2};

public static boolean exists(choices choice) {
   for(choice aChoice : choices.values()) {
      if(aChoice == choice) {
         return true;
      }
   }
   return false;
}

Редагувати:

Будь ласка, ознайомтеся з версією Річарда, як це більш доречно, оскільки це не буде працювати, якщо ви не перетворите його на використання Strings, що робить Річардс.


але я думаю, що ОП хоче перевірити рядок?
Річард Н

так, ха-ха. Цей метод не був би таким ефективним, оскільки ми вже знаємо, що вибір стоїть у перерахунках. Ваша модифікація правильніша.
jluzwick

1
Якщо ви хочете попрацювати над якоюсь підмножиною самих переліків (а не їх іменами), краще подивіться на EnumSet. download.oracle.com/javase/6/docs/api/java/util/EnumSet.html
Yishai

Можливо, дурне запитання, але чому це не .values()задокументовано на download.oracle.com/javase/6/docs/api/java/lang/Enum.html ?
Анонім

Це чудове запитання. Ви маєте рацію, що її немає в документації, і вона не існує в джерелі Enum. Я припускаю, що він присутній в одній з реалізацій Enum або є якесь правило JLS, яке це дозволяє. Також у всіх об'єктів колекції є це, і це можна розглядати як колекцію, хоча це не обов'язково впроваджувати колекцію.
jluzwick

3

Java Streams забезпечує елегантний спосіб зробити це

Stream.of(MyEnum.values()).anyMatch(v -> v.name().equals(strValue))

Повертає: true, якщо будь-які елементи потоку відповідають заданому значенню, інакше false


2

Чому б не поєднати відповідь Пабло з valueOf ()?

public enum Choices
{
    a1, a2, b1, b2;

    public static boolean contains(String s) {
        try {
            Choices.valueOf(s);
            return true;
        } catch (Exception e) {
            return false;
        }
}

Будь ласка, не варто. Дивіться іншу, старшу відповідь, еквівалентну вашій: stackoverflow.com/a/4936872/103412
Torsten

1

Цей підхід можна використовувати для перевірки будь-якого Enum, ви можете додати його до Utilsкласу:

public static <T extends Enum<T>> boolean enumContains(Class<T> enumerator, String value)
{
    for (T c : enumerator.getEnumConstants()) {
        if (c.name().equals(value)) {
            return true;
        }
    }
    return false;
}

Використовуйте його таким чином:

boolean isContained = Utils.enumContains(choices.class, "value");

1

Я створив наступний клас для цієї перевірки

public class EnumUtils {

    public static boolean isPresent(Enum enumArray[], String name) {
        for (Enum element: enumArray ) {
            if(element.toString().equals(name))
                return true;
        }
        return false;
    }

}

приклад використання:

public ArrivalEnum findArrivalEnum(String name) {

    if (!EnumUtils.isPresent(ArrivalEnum.values(), name))
        throw new EnumConstantNotPresentException(ArrivalEnum.class,"Arrival value must be 'FROM_AIRPORT' or 'TO_AIRPORT' ");

    return ArrivalEnum.valueOf(name);
}

0

Ви можете використовувати, valueOf("a1")якщо ви хочете шукати на String


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

Це призведе до виключення, якщо значення не існує
Richard H

Менш елегантний, ніж повторення через вибір enum, який шукає відповідний об’єкт?
jprete

0

Це перерахунок, це постійні значення, тому, якщо його в операторі переключення, він просто робить щось подібне:

case: val1
case: val2

Також навіщо вам знати, що оголошується константою?


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

@Jared це має набагато більше сенсу
Woot4Moo

@Jred, однак, це не має особливого значення, оскільки ви вже знаєте значення, які там є. В основному, еквівалент переліку list.contains () - MyEnum.MyAwesomeValue
Woot4Moo

0

З гуавою ще простіше:

boolean isPartOfMyEnum(String myString){

return Lists.newArrayList(MyEnum.values().toString()).contains(myString);

}

Як сказала Кіорія, це не спрацює. MyEnum.values()повертає масив екземплярів MyEnum і MyEnum.value().toString()повертає рядкове представлення цього об’єкта масиву (просто рядок типу "[LMyEnum; @ 15b94ed3")
користувач2137020

Вам потрібно зателефонувати .name()замість .toString()(якщо ви не перекриєте метод toString за замовчуванням). Дивіться це для отримання додаткової інформації: Різниця між перерахуванням .name () і .ToString ()
Gaʀʀʏ

0

Це поєднує в собі всі підходи попередніх методів і має мати рівноцінні показники. Він може використовуватися для будь-якого перерахунку, вказує рішення "Редагувати" від @Richard H і використовує винятки для недійсних значень, таких як @bestsss. Єдиний компроміс полягає в тому, що клас потрібно вказати, але це перетворює це на дворівневий.

import java.util.EnumSet;

public class HelloWorld {

static enum Choices {a1, a2, b1, b2}

public static <E extends Enum<E>> boolean contains(Class<E> _enumClass, String value) {
    try {
        return EnumSet.allOf(_enumClass).contains(Enum.valueOf(_enumClass, value));    
    } catch (Exception e) {
        return false; 
    }
}

public static void main(String[] args) {
    for (String value : new String[] {"a1", "a3", null}) {
        System.out.println(contains(Choices.class, value));
    }
}

}


0
com.google.common.collect.Sets.newHashSet(MyEnum.values()).contains("myValue")

0

рішення, щоб перевірити, чи є значення, а також отримати значення enum у відповідь:

protected TradeType getEnumType(String tradeType) {
    if (tradeType != null) {
        if (EnumUtils.isValidEnum(TradeType.class, tradeType)) {
            return TradeType.valueOf(tradeType);
        }
    }
    return null;
}

0

Цей для мене працює:

Arrays.asList(YourEnum.values()).toString().contains("valueToCheck");

3
Ваша версія повернеться істинною, навіть якщо YourEnum містить "valueToCheckBlaBla", оскільки "valueToCheck" буде присутній у рядковому поданні всього списку.
Нікко

0

Якщо ви використовуєте Java 8 або вище, ви можете зробити це:

boolean isPresent(String testString){
      return Stream.of(Choices.values()).map(Enum::name).collect(Collectors.toSet()).contains(testString);
}


0

Ви можете зробити його як метод містить:

enum choices {a1, a2, b1, b2};
public boolean contains(String value){
    try{
        EnumSet.allOf(choices.class).contains(Enum.valueOf(choices.class, value));
        return true;
    }catch (Exception e) {
        return false;
    }
}

або ви можете просто використовувати його з кодом блоку:

try{
    EnumSet.allOf(choices.class).contains(Enum.valueOf(choices.class, "a1"));
    //do something
}catch (Exception e) {
    //do something else
}

0

Ви також можете використовувати: com.google.common.base.Enums

Enums.getIfPresent (varEnum.class, varToLookFor) повертає необов'язково

Enums.getIfPresent (fooEnum.class, myVariable) .isPresent ()? Enums.getIfPresent (fooEnum.class, myVariable) .get: fooEnum.OTHERS


0

Я просто писав би,

Arrays.stream(Choice.values()).map(Enum::name).collect(Collectors.toList()).contains("a1");

Enum # equals працює лише з порівнянням об'єктів.


-1
public boolean contains(Choices value) {
   return EnumSet.allOf(Choices.class).contains(value);
}

це не спрацює. set мають об'єкти enum, а ви перевіряєте їх на рядок.
iTake

тепер відповідь не відповідає питанню, як це
стосувалося

-11

enumдосить потужні на Яві. Ви можете легко додати containsметод до перерахунку (як ви додали метод до класу):

enum choices {
  a1, a2, b1, b2;

  public boolean contains(String s)
  {
      if (s.equals("a1") || s.equals("a2") || s.equals("b1") || s.equals("b2")) 
         return true;
      return false;
  } 

};

ти мав на увазі s.equals("b1") || s.equals("b2")??
Jigar Joshi

3
Це, мабуть, не буде найкращим способом зробити це, оскільки вам доведеться додавати нові s.equals("xx")для кожного перерахунку, який ви додасте згодом.
jluzwick

1
І буде 1000+ перерахунків.
Джаред

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