Java: Verifique se o enum contém uma determinada string?

169

Aqui está o meu problema - estou procurando (se é que existe) o equivalente enum de ArrayList.contains();.

Aqui está uma amostra do meu problema de código:

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

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

Agora, percebo que uma ArrayListdas Stringsrotas seria a melhor aqui, mas preciso executar o conteúdo da minha enum por meio de um switch / case em outro lugar. Daí o meu problema.

Supondo que algo assim não exista, como eu poderia fazê-lo?

Jared
fonte
Switch / case com cordas é implementado a partir de Java 7
AndreyP

Respostas:

206

Isso deve servir:

public static boolean contains(String test) {

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

    return false;
}

Dessa forma, você não precisa se preocupar em adicionar valores adicionais de enum posteriormente, todos eles serão verificados.

Edit: Se a enumeração for muito grande, você poderá colar os valores em um HashSet:

public static HashSet<String> getEnums() {

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

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

  return values;
}

Então você pode simplesmente fazer: o values.contains("your string")que retorna verdadeiro ou falso.

Richard H
fonte
12
isso é um impl muito pobre .: Choice.valueOf (teste) é o que você quer (w / try / catch)
bestsss
17
bestsss, essa é claramente a solução mais apropriada. Lançar uma exceção para implementar um tipo de método existe () é uma prática ruim. Embora você possa pensar que sua implementação é mais eficiente devido ao fato de ela não parecer O (n), ela está na estrutura subjacente que não é visível. O uso de try {} catch adiciona sobrecarga. Além disso, simplesmente não é bonito.
jluzwick
25
@ Jared, definitivamente valueOf. Basta pegar sua exceção e retornar falso. Para quem diz o contrário, se você observar a implementação, ela já usa um mapa e os desenvolvedores do JDK têm uma chance muito melhor de otimizar isso. A API lança uma exceção, que é uma prática discutível (em vez de retornar nula), mas quando você lida com uma API que lança uma exceção, siga em frente, não reinvente a roda.
Yishai
2
A sobrecarga try / catch do @jluzwick é uma instrução de salto simples quando não é executada, mas o uso da exceção no bloco catch também é otimizado. Temer causa de tentativa / captura de perda de desempenho é uma prática ruim.
bestsss
22
contains () deve ser preferido sobre valueOf () com uma exceção. Por quê? Como "exceções, como o próprio nome indica, devem ser usadas apenas para condições excepcionais; nunca devem ser usadas para o fluxo de controle comum" (Joshua Bloch, "Java Efetivo").
James.garriss
226

Use o apache commons lang3 lib

 EnumUtils.isValidEnum(MyEnum.class, myValue)
RaphC
fonte
33
Observe que para os interessados, a implementação subjacente que eles usaram é simplesmente a solução try / catch (@since 3.0 @version $ Id: EnumUtils.java 1199894 2011-11-09 17: 53: 59Z ggregory $).
Jonathan Gawrych
1
Torna meu código mais simples, então eu não me importo se ele usa exceção para controle de fluxo (eles realmente não deveriam) ... Seria bom se eles mudassem isso.
Jpangamarca
1
A goiaba também contém uma solução como esta?
Cypress Frankenfeld
50

Você pode usar 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
  }
}

Se você espera que a verificação falhe com frequência, é melhor usar um loop simples, como outros demonstraram - se suas enumerações contiverem muitos valores, talvez builda HashSetou semelhante aos valores de enumeração convertidos em uma string e, em HashSetvez disso, consultar isso .

n
fonte
8
Eu não acho que a exceção seja a melhor escolha nesse caso.
precisa saber é o seguinte
6
Try and Catch deve ser o último recurso. Try and Catch são caros
Jesus Dimrix
2
confiar em exceções de tempo de execução para fazer lógica de negócios, além de caro, não é tão legível. em relação às exceções verificadas, é diferente, porque elas fazem parte do negócio.
Luís Soares
2
Isso também impede que alguém ative uma ampla interrupção ao lançar exceções para encontrar casos excepcionais reais que estão sendo repetidos. (ou pelo menos torna muito irritante). Use exceções para casos excepcionais.
Nickolay Kondratyev 29/01
4
EnumUtils.isValidEnum(MyEnum.class, myValue)usa uma lógica semelhante e IMO faz sentido para adicionar uma biblioteca inteira para esta tarefa trivial
Vikramjit Roy
37

Se você estiver usando o Java 1.8, poderá escolher o Stream + Lambda para implementar isso:

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);
Hao Ma
fonte
19

Melhor ainda:

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

};
Devashish Bansal
fonte
Obrigado por solução otimista
Parth Patel
19

Goiabas Enums poderia ser seu amigo

Como por exemplo:

enum MyData {
    ONE,
    TWO
}

@Test
public void test() {

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

Você pode primeiro converter a enumeração em Lista e, em seguida, usar o método list contains

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
}
Bikram
fonte
Essa deve ser a resposta aceita. A conversão para lista é a maneira mais limpa de fazer isso. Isso poupa toda a discussão (lateral) sobre "uso adequado de exceções" em Java nas outras respostas aqui.
Manuel
Lance IllegalArgumentException se o valor não existir.
mkyong 9/07/19
10

Poucas premissas:
1) Nenhuma tentativa / captura, pois é um controle de fluxo excepcional
2) O método 'contém' deve ser rápido, pois geralmente é executado várias vezes.
3) O espaço não é limitado (comum para soluções comuns)

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);
    }
}
AndreyP
fonte
10

Algumas bibliotecas foram mencionadas aqui, mas sinto falta da que realmente estava procurando: Primavera!

Existe o ObjectUtils # containsConstant, que não diferencia maiúsculas de minúsculas por padrão, mas pode ser rígido se você desejar. É usado assim:

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

Nota: usei o método sobrecarregado aqui para demonstrar como usar a verificação sensível a maiúsculas e minúsculas. Você pode omitir o booleano para ter um comportamento sem distinção entre maiúsculas e minúsculas.

Tenha cuidado com enumerações grandes, pois elas não usam a implementação do Mapa, como algumas fazem ...

Como bônus, ele também fornece uma variante que não diferencia maiúsculas de minúsculas do valueOf: ObjectUtils # caseInsensitiveValueOf

Pim Hazebroek
fonte
7

Você pode usar isso

YourEnum {A1, A2, B1, B2}

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

A atualização sugerida por @ wightwulf1944 foi incorporada para tornar a solução mais eficiente.

greperror
fonte
4
Esta implementação é ineficiente. Isso itera pelos valores da enumeração para criar um novo conjunto e, em seguida, cria um fluxo, que itera pelo conjunto resultante. Isso significa que um novo conjunto e fluxo é criado toda vez que a função é chamada, e usar stream()um conjunto significa que você está interagindo em cada elemento do conjunto em vez de tirar proveito da hashtable subjacente, que será mais rápida. Para melhorar isso, é melhor armazenar em cache o conjunto criado e usar seu contains()método. Se você precisar obter um fluxo, use-o Arrays.stream().
Subaru Tashiro
3

Eu não acho que exista, mas você pode fazer algo assim:

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

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

Editar:

Por favor, veja a versão de Richard disso, pois é mais apropriado, pois isso não funcionará, a menos que você a converta em Strings, o que Richards faz.

jluzwick
fonte
mas acho que o OP quer testar uma string?
Richard H
Sim, haha. Este método não seria tão eficaz como já sabemos que a escolha está nos enums. Sua modificação está mais correta.
jluzwick
1
Se você deseja trabalhar em algum subconjunto das enumerações em si (e não no nome delas), é melhor olhar para a EnumSet. download.oracle.com/javase/6/docs/api/java/util/EnumSet.html
Yishai
Talvez seja uma pergunta estúpida, mas por que não está .values()documentada em download.oracle.com/javase/6/docs/api/java/lang/Enum.html ?
Anónimo 8/11
Essa é uma ótima pergunta. Você está certo de que não existe na documentação e também não existe na fonte Enum. Estou assumindo que está presente em uma das implementações do Enum ou se existe alguma regra JLS que permita isso. Todos os objetos Collection também têm isso, e isso pode ser visto como uma coleção, mesmo que não esteja necessariamente implementando a coleção.
jluzwick
3

O Java Streams fornece uma maneira elegante de fazer isso

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

Retorna: true se algum elemento do fluxo corresponder ao valor fornecido, caso contrário, false

Sourabh
fonte
2

Por que não combinar a resposta de Pablo com um 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;
        }
}
Karthik. V
fonte
Por favor não. Veja outra resposta mais antiga, equivalente à sua: stackoverflow.com/a/4936872/103412
Torsten
1

Essa abordagem pode ser usada para verificar qualquer Enum, você pode adicioná-lo a uma Utilsclasse:

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

Use desta maneira:

boolean isContained = Utils.enumContains(choices.class, "value");
António Almeida
fonte
1

Eu criei a próxima aula para essa validação

public class EnumUtils {

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

}

exemplo de uso:

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);
}
Ignacio Jeria Garrido
fonte
0

Você pode usar valueOf("a1")se quiser procurar por String

Amir Afghani
fonte
3
mas isso não é elegante. se o valor não existir, gera uma exceção. então você provavelmente deve cercá-la com try catch
Kasturi
Que irá lançar uma exceção se o valor não existir
Richard H
Menos elegante do que percorrer as opções enum procurando um objeto correspondente?
jprete
0

É um enum, esses são valores constantes; portanto, se estiver em uma instrução switch, está apenas fazendo algo assim:

case: val1
case: val2

Além disso, por que você precisaria saber o que é declarado como constante?

Woot4Moo
fonte
Esse pedaço de código não está na declaração de switch, está em outro lugar. Eu estava simplesmente afirmando que, nesse caso, é necessário enum, já que outros sugeriram que eu use um ArrayList.
Jared
@Jared que faz muito mais sentido agora
Woot4Moo
@ Jared no entanto, isso realmente não importa, pois você já conhece os valores que existem. Basicamente, o equivalente enum de list.contains () é MyEnum.MyAwesomeValue
Woot4Moo
0

Com goiaba é ainda mais simples:

boolean isPartOfMyEnum(String myString){

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

}
chaiyachaiya
fonte
Como disse Kioria , isso não vai funcionar. MyEnum.values()retornos matriz de MyEnum instâncias e MyEnum.value().toString()representação retornos cadeia de este objecto matriz (apenas cadeia como "[LMyEnum; @ 15b94ed3")
user2137020
Você precisa chamar em .name()vez de .toString()(a menos que substitua o método toString padrão). Veja isto para obter mais informações: Diferença entre enum .name () e .toString ()
Gaʀʀʏ 15/04/16
0

Isso combina todas as abordagens dos métodos anteriores e deve ter desempenho equivalente. Ele pode ser usado para qualquer enumeração, destaca a solução "Editar" do @Richard H e usa Exceções para valores inválidos como @bestsss. A única desvantagem é que a classe precisa ser especificada, mas isso transforma isso em uma linha dupla.

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

}

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

solução para verificar se o valor está presente e obter o valor enum em troca:

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

Este funciona para mim:

Arrays.asList(YourEnum.values()).toString().contains("valueToCheck");
EDiaz
fonte
3
Sua versão retornará true, mesmo que YourEnum contenha "valueToCheckBlaBla", porque "valueToCheck" estará presente na representação de seqüência de caracteres da lista inteira.
Nicko
0

Se você estiver usando o Java 8 ou superior, poderá fazer o seguinte:

boolean isPresent(String testString){
      return Stream.of(Choices.values()).map(Enum::name).collect(Collectors.toSet()).contains(testString);
}
Chirag C
fonte
0
  Set.of(CustomType.values())
     .contains(customTypevalue) 
Dmitrii
fonte
0

Você pode fazer isso como um método contains:

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

ou você pode simplesmente usá-lo com seu bloco de código:

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

você também pode usar: com.google.common.base.Enums

Enums.getIfPresent (varEnum.class, varToLookFor) retorna um opcional

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

Rabhi salim
fonte
0

Eu apenas escreveria

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

Enum # equals só funciona com comparação de objetos.

Swadeshi
fonte
-1
public boolean contains(Choices value) {
   return EnumSet.allOf(Choices.class).contains(value);
}
Anton
fonte
isso não vai funcionar. set tem objetos enum, enquanto você está verificando se há uma string.
iTake
agora a resposta não cabe a pergunta, como se tratava de Cordas :)
iTake
-11

enumsão bastante poderosos em Java. Você pode facilmente adicionar um containsmétodo à sua enumeração (como você adicionaria um método a uma classe):

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

};
Pablo Santa Cruz
fonte
você quis dizer s.equals("b1") || s.equals("b2")??
precisa saber é o seguinte
3
Provavelmente, essa não será a melhor maneira de fazê-lo, pois você precisará adicionar novo s.equals("xx")para cada enum que adicionar posteriormente.
jluzwick
1
E haverá mais de 1000 enumerações.
Jared
20
Como as pessoas que sugerem soluções horríveis como essa ganham reputação de 64K? Eu odeio pensar de tudo o ser código de baixa qualidade pendurada lá fora, com base neste contribuintes respostas
Dexygen