Java: Проверить, содержит ли enum заданную строку?


169

Вот моя проблема - я ищу (если он вообще существует) перечислимый эквивалент ArrayList.contains();.

Вот пример моей проблемы с кодом:

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

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

Теперь я понимаю, что лучшим ArrayListиз Stringsних будет здесь, но мне нужно пропустить содержимое моего перечисления через переключатель / регистр в другом месте. Отсюда и моя проблема.

Если предположить, что чего-то подобного не существует, как я могу это сделать?


Переключатель / кейс со строками реализован начиная с Java 7
AndreyP

Ответы:


206

Это должно сделать это:

public static boolean contains(String test) {

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

    return false;
}

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

Изменить: Если перечисление очень большое, вы можете вставить значения в 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 (test) - это то, что вы хотите (w / try / catch)
bestsss

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

25
@ Джаред, определенно ценность. Просто поймайте свое исключение и верните false. Тем, кто говорит иначе, если вы посмотрите на реализацию, она уже использует Map, и у разработчиков JDK гораздо больше шансов оптимизировать это. API выдает исключение, что является дискуссионной практикой (вместо возврата null), но когда вы имеете дело с API, который выдает исключение, используйте его, не изобретайте колесо заново.
Ишай

2
@jluzwick try / catch overhead - это простая инструкция перехода, когда она не выполняется, также не оптимизируется использование исключений в блоке catch. Боязнь "попробовать / поймать" - причина потери производительности - плохая практика.
bestsss

23
Содержит () должен быть предпочтительнее, чем valueOf () за исключением. Зачем? Потому что «исключения, как следует из их названия, должны использоваться только для исключительных условий; они никогда не должны использоваться для обычного потока управления» (Джошуа Блох, «Эффективная Java»).
james.garriss

226

Вместо этого используйте Apache Commons lang3 lib

 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
Гуава также содержит решение как это?
Кипарис Франкенфельд

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
полагаться на исключения во время выполнения, чтобы делать бизнес-логику, кроме дорогой, не так читабельно. Что касается проверенных исключений, то они разные, потому что они составляют часть бизнеса.
Луис Соареш

2
Это также препятствует тому, чтобы кто-либо включал широкий разрыв исключений, чтобы найти реальные исключительные случаи, которые повторяются. (или, по крайней мере, это очень раздражает). Используйте исключения для исключительных случаев.
Николай Кондратьев

4
EnumUtils.isValidEnum(MyEnum.class, myValue)использует аналогичную логику, и IMO имеет смысл добавить целую библиотеку для этой тривиальной задачи
Викрамджит Рой

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

20

Guavas Enums может быть твоим другом

Например, вот так:

enum MyData {
    ONE,
    TWO
}

@Test
public void test() {

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

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

};

Спасибо за оптимистичное решение
Парт Патель

11

Вы можете сначала преобразовать enum в 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

Несколько предположений:
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);
    }
}

10

Здесь было упомянуто несколько библиотек, но я скучаю по той, которую я действительно искал: Spring!

Существует ObjectUtils # содержитConstant, который по умолчанию не учитывает регистр, но может быть строгим, если хотите. Используется так:

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

Примечание. Я использовал перегруженный метод, чтобы продемонстрировать, как использовать проверку с учетом регистра. Вы можете опустить логическое значение, чтобы поведение не чувствительно к регистру.

Будьте осторожны с большими перечислениями, поскольку они не используют реализацию Map, как это делают некоторые ...

В качестве бонуса он также предоставляет вариант valueOf: ObjectUtils # caseInsensitiveValueOf без учета регистра


7

Вы можете использовать это

YourEnum {A1, A2, B1, B2}

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

Обновление, предложенное @ wightwulf1944, включено, чтобы сделать решение более эффективным.


4
Эта реализация неэффективна. Это перебирает значения перечисления для создания нового набора, затем создает поток, который перебирает результирующий набор. Это означает, что каждый раз при вызове функции создается новый набор и поток, а использование stream()набора означает, что вы выполняете итерации по каждому элементу в наборе, а не используете базовую хеш-таблицу, которая будет быстрее. Чтобы улучшить это, лучше всего кэшировать созданный набор и использовать contains()вместо него его метод. Если вам нужно получить поток, используйте Arrays.stream()вместо этого.
Субару

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

Редактировать:

Пожалуйста, ознакомьтесь с версией Ричарда, так как она более уместна, так как это не сработает, если вы не конвертируете ее в строки, что делает Ричардс.


но я думаю, что ОП хочет проверить строку?
Ричард Х

да хаха Этот метод не будет настолько эффективным, поскольку мы уже знаем, что выбор в перечислениях. Ваша модификация более правильная.
jluzwick

1
Если вы хотите работать с некоторыми подмножествами перечислений (а не их именами), лучше взглянуть на EnumSet. download.oracle.com/javase/6/docs/api/java/util/EnumSet.html
Ишай,

Возможно глупый вопрос, но почему не .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
Торстен

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")если вы хотите посмотреть на строку


3
но это не элегантно .. если значение не существует, оно выдает исключение. так что вы, вероятно, должны окружить его попыткой поймать
Kasturi

Это вызовет исключение, если значение не существует
Ричард Х

Менее элегантно, чем перебирать варианты перечисления в поисках подходящего объекта?
jprete

0

Это перечисление, это постоянные значения, поэтому, если в выражении switch он просто делает что-то вроде этого:

case: val1
case: val2

Также зачем вам знать, что объявлено как константа?


Этот фрагмент кода отсутствует в указанном операторе switch, это в другом месте. Я просто констатировал, что в этом случае enum необходим, поскольку другие предложили вместо этого использовать ArrayList.
Джаред

@Jared, что теперь имеет гораздо больше смысла
Woot4Moo

@ Джаред, однако, это не имеет большого значения, так как вы уже знаете значения, которые там есть. В основном перечислимым эквивалентом 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»)
user2137020

Вам нужно вызвать .name()вместо .toString()(если вы не переопределите метод toString по умолчанию). См. Это для получения дополнительной информации: Разница между enum .name () и .toString ()
Gaʀʀʏ

0

Это объединяет все подходы из предыдущих методов и должно иметь эквивалентную производительность. Он может использоваться для любого перечисления, встраивает решение «Edit» из @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

Решение, чтобы проверить, присутствует ли значение, а также получить значение перечисления в ответ:

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
Ваша версия вернет true, даже если 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

теперь ответ не подходит к вопросу, как это было о String :)
iTake

-11

enumдовольно мощный в Java. Вы можете легко добавить 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")??
Джигар Джоши

3
Это, вероятно, не лучший способ сделать это, так как вам придется добавлять новые s.equals("xx")для каждого перечисления, которое вы добавите позже.
jluzwick

1
И будет более 1000 перечислений.
Джаред

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