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.