Java: Verifica se enum contiene una determinata stringa?


169

Ecco il mio problema: sto cercando (se esiste) l'equivalente enum di ArrayList.contains();.

Ecco un esempio del mio problema con il codice:

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

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

Ora, mi rendo conto che una ArrayListdi Stringssarebbe la strada migliore qui, ma devo eseguire il mio contenuto enum attraverso un interruttore / caso altrove. Da qui il mio problema.

Supponendo che qualcosa del genere non esista, come potrei farlo?


Switch / case con stringhe è implementato a partire da Java 7
AndreyP,

Risposte:


206

Questo dovrebbe farlo:

public static boolean contains(String test) {

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

    return false;
}

In questo modo significa che non devi preoccuparti di aggiungere ulteriori valori enum in un secondo momento, sono tutti controllati.

Modifica: se l'enum è molto grande potresti attaccare i valori in un HashSet:

public static HashSet<String> getEnums() {

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

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

  return values;
}

Quindi puoi semplicemente fare: values.contains("your string")che restituisce vero o falso.


12
è un impianto molto scarso .: Choice.valueOf (test) è quello che vuoi (w / try / catch)
bestsss

17
bestsss, questa è chiaramente la soluzione più appropriata. Generare un'eccezione per implementare un tipo di metodo esiste () è una cattiva pratica. Mentre potresti pensare che la tua implementazione sia più efficiente perché non sembra O (n), è nel framework sottostante che non è visibile. Anche l'utilizzo di try {} catch aggiunge un sovraccarico. Inoltre, non è carino.
jluzwick,

25
@Jared, sicuramente valueOf. Prendi la tua eccezione e restituisci false. A chi dice il contrario, se si guarda all'implementazione, utilizza già una mappa e gli sviluppatori JDK hanno molte più possibilità di ottimizzarla. L'API genera un'eccezione, che è una pratica discutibile (invece di restituire null), ma quando si tratta di un'API che genera un'eccezione, procedere con essa, non reinventare la ruota.
Yishai,

2
@jluzwick try / catch overhead è una semplice istruzione di salto quando non viene presa, inoltre non è ottimizzata l'eccezione nel blocco catch. Il timore di provare / catturare la causa della perdita di prestazione è una cattiva pratica.
bestsss

22
contiene () è da preferire a valueOf () con un'eccezione. Perché? Perché "le eccezioni, come suggerisce il loro nome, devono essere utilizzate solo per condizioni eccezionali; non dovrebbero mai essere utilizzate per il normale flusso di controllo" (Joshua Bloch, "Effective Java").
james.garriss,

226

Utilizzare invece la lib lang3 di commons Apache

 EnumUtils.isValidEnum(MyEnum.class, myValue)

33
Nota per gli interessati, l'implementazione sottostante che hanno usato è semplicemente la soluzione try / catch (@since 3.0 @version $ Id: EnumUtils.java 1199894 2011-11-09 17: 53: 59Z ggregory $).
Jonathan Gawrych,

1
Rende il mio codice più semplice, quindi non mi interessa se utilizza un'eccezione per il controllo del flusso (in realtà non dovrebbero) ... Sarebbe bello se lo cambiassero.
jpangamarca,


1
La guava contiene anche una soluzione come questa?
Cypress Frankenfeld,

50

Puoi usare 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 ti aspetti che il controllo fallisca spesso, potresti fare meglio a usare un semplice ciclo come altri hanno mostrato - se i tuoi enum contengono molti valori, forse builda HashSeto simili dei tuoi valori enum convertiti in una stringa e interrogali HashSetinvece.


8
Non credo che le eccezioni siano la scelta migliore in questo caso.
GokcenG

6
Try and Catch dovrebbe essere l'ultima risorsa. Try and Catch sono troppo costosi
Jesus Dimrix,

2
basarsi su eccezioni di runtime per fare la logica di business, oltre che costoso, non è così leggibile. per quanto riguarda le eccezioni verificate, è diverso, perché fanno parte del business.
Luís Soares,

2
Ciò impedisce anche a chiunque di attivare un'ampia interruzione delle eccezioni che vengono lanciate per trovare casi eccezionali reali che vengono ritentati. (o almeno lo rende molto fastidioso). Usa le eccezioni per casi eccezionali.
Nickolay Kondratyev,

4
EnumUtils.isValidEnum(MyEnum.class, myValue)usa una logica simile e IMO ha senso aggiungere un'intera libreria per questo compito banale
Vikramjit Roy,

37

Se stai usando Java 1.8, puoi scegliere Stream + Lambda per implementare questo:

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

Anche meglio:

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

};

Grazie per la soluzione ottimista
Parth Patel,

19

Guavas Enums potrebbe essere tuo amico

Come ad esempio questo:

enum MyData {
    ONE,
    TWO
}

@Test
public void test() {

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

11

È possibile prima convertire l'enum in elenco e quindi utilizzare il metodo list contiene

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
}

Questa dovrebbe essere la risposta accettata. La conversione in elenco è il modo più pulito per farlo. Risparmia l'intera discussione (laterale) sul "corretto uso delle eccezioni" in Java nelle altre risposte qui.
Manuel,

Genera IllegalArgumentException se il valore non esiste.
mkyong,

10

Pochi presupposti:
1) Nessun tentativo / cattura, in quanto è un eccezionale controllo del flusso
2) Il metodo "contiene" deve essere rapido, poiché di solito viene eseguito più volte.
3) Lo spazio non è limitato (comune per soluzioni ordinarie)

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

Qui sono state menzionate un paio di librerie, ma mi manca quella che stavo effettivamente cercando: Spring!

C'è ObjectUtils # IncludesConstant che non distingue tra maiuscole e minuscole per impostazione predefinita, ma può essere rigoroso se lo si desidera. Si usa così:

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

Nota: ho usato il metodo sovraccarico qui per dimostrare come utilizzare il controllo con distinzione tra maiuscole e minuscole. Puoi omettere il valore booleano per avere un comportamento insensibile al maiuscolo / minuscolo.

Fai attenzione con gli enumeratori di grandi dimensioni, poiché non usano l'implementazione di Map come alcuni fanno ...

Come bonus, fornisce anche una variante senza distinzione tra maiuscole e minuscole del valoreOf: ObjectUtils # caseInsensitiveValueOf


7

Puoi usare questo

YourEnum {A1, A2, B1, B2}

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

L'aggiornamento suggerito da @ wightwulf1944 è incorporato per rendere la soluzione più efficiente.


4
Questa implementazione è inefficiente. Ciò scorre attraverso i valori enum per creare un nuovo set, quindi crea un flusso, che scorre attraverso il set risultante. Ciò significa che un nuovo set e flusso vengono creati ogni volta che viene chiamata la funzione e l'utilizzo stream()su un set significa che stai iterando su ogni elemento nel set invece di sfruttare la tabella hash sottostante che sarà più veloce. Per migliorare questo, è meglio memorizzare nella cache il set creato e utilizzare invece il suo contains()metodo. Se è necessario ottenere uno stream, utilizzare Arrays.stream()invece.
Subaru Tashiro,

3

Non penso che ci sia, ma puoi fare qualcosa del genere:

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

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

Modificare:

Si prega di vedere la versione di Richard di questo in quanto è più appropriato in quanto non funzionerà se non lo si converte per utilizzare le stringhe, cosa che Richards fa.


ma penso che l'OP voglia testare una stringa?
Richard H,

sì, ahah. Questo metodo non sarebbe così efficace come sappiamo già che la scelta è negli enum. La tua modifica è più corretta.
jluzwick,

1
Se vuoi lavorare su alcuni sottogruppi degli enum stessi (e non sui loro nomi), meglio guardare EnumSet. download.oracle.com/javase/6/docs/api/java/util/EnumSet.html
Yishai

Forse una domanda stupida, ma perché non è .values()documentato su download.oracle.com/javase/6/docs/api/java/lang/Enum.html ?
Anonimo

Questa è un'ottima domanda. Hai ragione, non esiste nella documentazione e non esiste nemmeno nella fonte Enum. Suppongo che sia presente in una delle implementazioni di Enum o che ci sia qualche regola JLS che lo consenta. Anche tutti gli oggetti Collection hanno questo, e questo può essere visto come una Collection anche se non implementa necessariamente Collection.
jluzwick,

3

Java Streams offre un modo elegante per farlo

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

Restituisce: vero se qualche elemento del flusso corrisponde al valore fornito, altrimenti falso


2

Perché non combinare la risposta di Pablo con un 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;
        }
}

Per favore, no. Vedi altre risposte precedenti equivalenti alle tue: stackoverflow.com/a/4936872/103412
Torsten

1

Questo approccio può essere utilizzato per controllare qualsiasi Enum, è possibile aggiungerlo a una 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;
}

Usalo in questo modo:

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

1

Ho creato la classe successiva per questa convalida

public class EnumUtils {

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

}

esempio di utilizzo:

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

Puoi usarlo valueOf("a1")se vuoi cercare String


3
ma questo non è elegante .. se il valore non esiste genera un'eccezione. quindi probabilmente devi circondarlo con provare a catturare
Kasturi

Ciò genererà un'eccezione se il valore non esiste
Richard H

Meno elegante dell'iterare attraverso le scelte enum che cercano un oggetto corrispondente?
giovedì

0

È un enum, quelli sono valori costanti, quindi se in una dichiarazione switch sta facendo qualcosa del genere:

case: val1
case: val2

Inoltre, perché dovresti sapere cosa viene dichiarato come costante?


Questo bit di codice non è nella suddetta istruzione switch, che è altrove. Stavo semplicemente affermando che in questo caso, enum è necessario poiché altri avevano suggerito di usare invece una ArrayList.
Jared,

@Jared che ora ha molto più senso
Woot4Moo

@Jared comunque non ha molta importanza dato che conosci già i valori che ci sono. Fondamentalmente l'equivalente enum di list.contains () è MyEnum.MyAwesomeValue
Woot4Moo

0

Con la guava è ancora più semplice:

boolean isPartOfMyEnum(String myString){

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

}

Come ha detto Kioria, questo non funzionerà. MyEnum.values()restituisce un array di istanze MyEnum e MyEnum.value().toString()restituisce una rappresentazione in forma di stringa di questo oggetto array (solo una stringa del tipo "[LMyEnum; @ 15b94ed3")
user2137020

È necessario chiamare .name()invece di .toString()(a meno che non si ignori il metodo toString predefinito). Vedi questo per maggiori informazioni: Differenza tra enum .name () e .toString ()
Gaʀʀʏ

0

Questo combina tutti gli approcci dei metodi precedenti e dovrebbe avere prestazioni equivalenti. Può essere utilizzato per qualsiasi enum, incorpora la soluzione "Modifica" di @Richard H e utilizza le eccezioni per valori non validi come @bestsss. L'unico compromesso è che la classe deve essere specificata, ma questo lo trasforma in un doppio.

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

soluzione per verificare se il valore è presente e ottenere in cambio valore enum:

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

0

Questo funziona per me:

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

3
La tua versione restituirà true anche se YourEnum contiene "valueToCheckBlaBla", perché "valueToCheck" sarà presente nella rappresentazione di stringa dell'intero elenco.
Nicko,

0

Se si utilizza Java 8 o versioni successive, è possibile effettuare questa operazione:

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

0
  Set.of(CustomType.values())
     .contains(customTypevalue) 

0

Puoi farlo come un metodo contiene:

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

oppure puoi semplicemente usarlo con il tuo blocco di codice:

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

0

puoi anche utilizzare: com.google.common.base.Enums

Enums.getIfPresent (varEnum.class, varToLookFor) restituisce un Opzionale

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


0

Vorrei solo scrivere,

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

Enum # equals funziona solo con il confronto di oggetti.


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

non funzionerà. set ha oggetti enum, mentre lo stai controllando per una stringa.
iTake il

ora la risposta non si adatta alla domanda, poiché si trattava di String :)
iTake,

-11

enumsono piuttosto potenti in Java. Puoi facilmente aggiungere un containsmetodo al tuo enum (come aggiungeresti un metodo a una 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;
  } 

};

intendevi s.equals("b1") || s.equals("b2")??
Jigar Joshi,

3
Questo probabilmente non sarà il modo migliore per farlo poiché dovrai aggiungere nuovi s.equals("xx")per ogni enum che aggiungerai in seguito.
jluzwick,

1
E ci saranno oltre 1000 enumerazioni.
Jared,

20
In che modo le persone che suggeriscono soluzioni orribili come questa ottengono la reputazione di 64 KB? Odio pensare a tutto il codice schifoso che viene lanciato là fuori in base alle risposte di questi collaboratori
Dexygen
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.