Java: Überprüfen Sie, ob enum eine bestimmte Zeichenfolge enthält?

169

Hier ist mein Problem - ich suche (falls es überhaupt existiert) das Enum-Äquivalent von ArrayList.contains();.

Hier ist ein Beispiel für mein Codeproblem:

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

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

Jetzt ist mir klar, dass ein ArrayListvon Stringshier der bessere Weg wäre, aber ich muss meinen Enum-Inhalt durch einen Schalter / Fall an einer anderen Stelle laufen lassen. Daher mein Problem.

Angenommen, so etwas gibt es nicht. Wie könnte ich das tun?

Jared
quelle
Switch / Case mit Strings wird ab Java 7
AndreyP

Antworten:

205

Dies sollte es tun:

public static boolean contains(String test) {

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

    return false;
}

Auf diese Weise müssen Sie sich später keine Gedanken mehr über das Hinzufügen zusätzlicher Aufzählungswerte machen. Alle werden überprüft.

Bearbeiten: Wenn die Aufzählung sehr groß ist, können Sie die Werte in ein HashSet einfügen:

public static HashSet<String> getEnums() {

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

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

  return values;
}

Dann können Sie einfach Folgendes tun: values.contains("your string")was true oder false zurückgibt.

Richard H.
quelle
12
Das ist eine sehr schlechte Implikation: Choice.valueOf (Test) ist das, was Sie wollen (w / try / catch)
bestsss
17
Dies ist eindeutig die am besten geeignete Lösung. Das Auslösen einer Ausnahme zum Implementieren einer existierenden Methode () ist eine schlechte Praxis. Während Sie vielleicht denken, dass Ihre Implementierung effizienter ist, weil sie nicht O (n) aussieht, befindet sie sich im zugrunde liegenden Framework, das nicht sichtbar ist. Auch die Verwendung von try {} catch erhöht den Overhead. Außerdem ist es einfach nicht schön.
Jluzwick
25
@ Jared, definitiv valueOf. Fangen Sie einfach Ihre Ausnahme ab und geben Sie false zurück. Für diejenigen, die etwas anderes sagen, wenn Sie sich die Implementierung ansehen, wird bereits eine Map verwendet, und die JDK-Entwickler haben eine viel bessere Chance, dies zu optimieren. Die API löst eine Ausnahme aus, was eine umstrittene Praxis ist (anstatt null zurückzugeben). Wenn Sie sich jedoch mit einer API befassen, die eine Ausnahme auslöst, erfinden Sie das Rad nicht neu.
Yishai
2
@jluzwick Try / Catch Overhead ist eine einfache Sprunganweisung, wenn sie nicht ausgeführt wird. Die Verwendung der Ausnahme im catch-Block wird ebenfalls optimiert. Die Angst vor einem Leistungsausfall ist eine schlechte Praxis.
Bestsss
22
enthält () ist mit Ausnahme von valueOf () vorzuziehen. Warum? Weil "Ausnahmen, wie der Name schon sagt, nur für außergewöhnliche Bedingungen verwendet werden dürfen; sie sollten niemals für den normalen Kontrollfluss verwendet werden" (Joshua Bloch, "Effective Java").
James.garriss
226

Verwenden Sie stattdessen die Apache Commons Lang3 Lib

 EnumUtils.isValidEnum(MyEnum.class, myValue)
RaphC
quelle
33
Hinweis für Interessenten: Die zugrunde liegende Implementierung, die sie verwendet haben, ist einfach die Try / Catch-Lösung (@since 3.0 @version $ Id: EnumUtils.java 1199894 2011-11-09 17: 53: 59Z ggregory $).
Jonathan Gawrych
1
Vereinfacht meinen Code, daher ist es mir egal, ob er eine Ausnahme für die Flusskontrolle verwendet (das sollten sie wirklich nicht) ... Es wäre schön, wenn sie das ändern würden.
Jpangamarca
1
Enthält Guave auch eine solche Lösung?
Cypress Frankenfeld
50

Sie können verwenden 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
  }
}

Wenn Sie erwarten, dass die Prüfung häufig fehlschlägt, ist es möglicherweise besser, eine einfache Schleife zu verwenden, wie andere gezeigt haben. Wenn Ihre Aufzählungen viele Werte enthalten , konvertieren Sie möglicherweise Builda HashSetoder ähnliche Ihrer Aufzählungswerte in eine Zeichenfolge und fragen Sie diese ab HashSet.

nr
quelle
8
Ich denke nicht, dass Ausnahmen in einem solchen Fall die beste Wahl sind.
GokcenG
6
Try and Catch sollte das letzte Mittel sein. Try and Catch sind zu teuer
Jesus Dimrix
2
Das Verlassen auf Laufzeitausnahmen zur Ausführung von Geschäftslogik ist nicht nur teuer, sondern auch nicht so lesbar. In Bezug auf geprüfte Ausnahmen ist dies anders, da diese Teil des Geschäfts sind.
Luís Soares
2
Dies verhindert auch, dass jemand eine breite Pause einlegt, wenn Ausnahmen ausgelöst werden, um tatsächliche Ausnahmefälle zu finden, die wiederholt werden. (oder macht es zumindest sehr nervig). Verwenden Sie Ausnahmen für Ausnahmefälle.
Nickolay Kondratyev
4
EnumUtils.isValidEnum(MyEnum.class, myValue)verwendet eine ähnliche Logik und IMO ist es sinnvoll, eine ganze Bibliothek für diese triviale Aufgabe
hinzuzufügen
37

Wenn Sie Java 1.8 verwenden, können Sie Stream + Lambda auswählen, um dies zu implementieren:

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
quelle
19

Guavas Enums könnte dein Freund sein

Wie zB das:

enum MyData {
    ONE,
    TWO
}

@Test
public void test() {

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

Noch besser:

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
quelle
Vielen Dank für die optimistische Lösung
Parth Patel
11

Sie können zuerst die Aufzählung in Liste konvertieren und dann die Methode Liste enthält verwenden

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
quelle
Dies sollte die akzeptierte Antwort sein. Die Konvertierung in eine Liste ist der sauberste Weg, dies zu tun. Es erspart die gesamte (Neben-) Diskussion über die "ordnungsgemäße Verwendung von Ausnahmen" in Java in den anderen Antworten hier.
Manuel
Lösen Sie eine IllegalArgumentException aus, wenn der Wert nicht vorhanden ist.
Mkyong
10

Ein paar Bibliotheken wurden hier erwähnt, aber ich vermisse die, nach der ich eigentlich gesucht habe: Frühling!

Es gibt die ObjectUtils # includesConstant, bei der die Groß- und Kleinschreibung standardmäßig nicht berücksichtigt wird, die jedoch bei Bedarf streng sein kann. Es wird so verwendet:

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

Hinweis: Ich habe hier die überladene Methode verwendet, um die Verwendung der Groß- und Kleinschreibung zu demonstrieren. Sie können den Booleschen Wert weglassen, um die Groß- und Kleinschreibung nicht zu berücksichtigen.

Seien Sie jedoch vorsichtig mit großen Aufzählungen, da diese die Map-Implementierung nicht wie manche verwenden ...

Als Bonus bietet es auch eine Variante von valueOf: ObjectUtils # caseInsensitiveValueOf, bei der die Groß- und Kleinschreibung nicht berücksichtigt wird

Pim Hazebroek
quelle
9

Einige Annahmen:
1) Kein Versuch / Fang, da es sich um eine außergewöhnliche Flusskontrolle handelt.
2) Die Methode "enthält" muss schnell sein, da sie normalerweise mehrmals ausgeführt wird.
3) Der Platz ist nicht begrenzt (üblich für gewöhnliche Lösungen)

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
quelle
7

Sie können dies verwenden

YourEnum {A1, A2, B1, B2}

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

Das von @ wightwulf1944 vorgeschlagene Update wurde integriert, um die Lösung effizienter zu gestalten.

greperror
quelle
4
Diese Implementierung ist ineffizient. Dies durchläuft die Aufzählungswerte, um eine neue Menge zu erstellen, und erstellt dann einen Stream, der die resultierende Menge durchläuft. Dies bedeutet, dass bei jedem Aufruf der Funktion ein neuer Satz und ein neuer Stream erstellt stream()werden. Wenn Sie einen Satz verwenden, iterieren Sie für jedes Element im Satz, anstatt die zugrunde liegende Hashtabelle zu nutzen, die schneller ist. Um dies zu verbessern, ist es am besten, das erstellte Set zwischenzuspeichern und contains()stattdessen seine Methode zu verwenden. Wenn Sie einen Stream erhalten müssen, verwenden Sie Arrays.stream()stattdessen.
Subaru Tashiro
3

Ich glaube nicht, aber Sie können so etwas tun:

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

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

Bearbeiten:

Bitte lesen Sie Richards Version davon, da dies angemessener ist, da dies nur funktioniert, wenn Sie es in Strings konvertieren, was Richards tut.

jluzwick
quelle
aber ich denke das OP will einen String testen?
Richard H
ja haha. Diese Methode wäre nicht so effektiv, da wir bereits wissen, dass die Auswahl in den Aufzählungen liegt. Ihre Änderung ist korrekter.
Jluzwick
1
Wenn Sie an einer Teilmenge der Aufzählungen selbst (und nicht an ihren Namen) arbeiten möchten, schauen Sie sich besser EnumSet an. download.oracle.com/javase/6/docs/api/java/util/EnumSet.html
Yishai
Vielleicht eine blöde Frage, aber warum ist die .values()unter download.oracle.com/javase/6/docs/api/java/lang/Enum.html nicht dokumentiert ?
Anonym
Das ist eine gute Frage. Sie haben Recht, es existiert nicht in der Dokumentation und es existiert auch nicht in der Enum-Quelle. Ich gehe davon aus, dass es in einer der Implementierungen von Enum vorhanden ist oder dass es eine JLS-Regel gibt, die dies zulässt. Auch alle Collection-Objekte haben dies, und dies kann als Collection betrachtet werden, obwohl es nicht unbedingt Collection implementiert.
Jluzwick
3

Java Streams bietet eine elegante Möglichkeit, dies zu tun

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

Rückgabe: true, wenn Elemente des Streams mit dem angegebenen Wert übereinstimmen, andernfalls false

Sourabh
quelle
2

Warum nicht Pablos Antwort mit einem valueOf () kombinieren?

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.
quelle
Bitte nicht. Siehe andere, ältere Antwort, die Ihrer entspricht: stackoverflow.com/a/4936872/103412
Torsten
1

Dieser Ansatz kann verwendet werden, um alle zu überprüfen Enum. Sie können ihn einer UtilsKlasse hinzufügen :

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

Verwenden Sie es so:

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

Ich habe die nächste Klasse für diese Validierung erstellt

public class EnumUtils {

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

}

Anwendungsbeispiel:

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
quelle
0

Sie können verwenden, valueOf("a1")wenn Sie nach String suchen möchten

Amir Afghani
quelle
3
aber das ist nicht elegant .. wenn der Wert nicht existiert, löst er eine Ausnahme aus. Also müssen Sie es wahrscheinlich mit Try Catch umgeben
Kasturi
Das wird eine Ausnahme auslösen, wenn der Wert nicht existiert
Richard H
Weniger elegant als das Durchlaufen der Aufzählungsoptionen auf der Suche nach einem passenden Objekt?
jprete
0

Es ist eine Aufzählung, das sind konstante Werte. Wenn es also in einer switch-Anweisung steht, macht es einfach so etwas:

case: val1
case: val2

Warum sollten Sie auch wissen müssen, was als Konstante deklariert ist?

Woot4Moo
quelle
Dieses Codebit ist nicht in der switch-Anweisung enthalten, das ist anderswo. Ich habe lediglich festgestellt, dass in diesem Fall eine Aufzählung erforderlich ist, da andere vorgeschlagen haben, stattdessen eine ArrayList zu verwenden.
Jared
@ Jared das macht jetzt viel mehr Sinn
Woot4Moo
@Jared ist jedoch nicht wirklich wichtig, da Sie bereits die Werte kennen, die dort sind. Grundsätzlich ist das Enum-Äquivalent von list.contains () MyEnum.MyAwesomeValue
Woot4Moo
0

Mit Guave ist es noch einfacher:

boolean isPartOfMyEnum(String myString){

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

}
Chaiyachaiya
quelle
Wie Kioria sagte, wird das nicht funktionieren. MyEnum.values()Gibt ein Array von MyEnum-Instanzen zurück und MyEnum.value().toString()gibt eine Zeichenfolgendarstellung dieses Array-Objekts zurück (nur Zeichenfolge wie "[LMyEnum; @ 15b94ed3")
user2137020
Sie müssen .name()statt aufrufen .toString()(es sei denn, Sie überschreiben die Standardmethode toString). Weitere Informationen finden Sie hier: Unterschied zwischen enum .name () und .toString ()
Gaʀʀʏ
0

Dies kombiniert alle Ansätze früherer Methoden und sollte eine gleichwertige Leistung aufweisen. Es kann für jede Aufzählung verwendet werden, fügt die "Edit" -Lösung von @Richard H ein und verwendet Ausnahmen für ungültige Werte wie @bestsss. Der einzige Nachteil ist, dass die Klasse angegeben werden muss, aber dies macht dies zu einem Zweiliner.

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
quelle
0
com.google.common.collect.Sets.newHashSet(MyEnum.values()).contains("myValue")
cnmuc
quelle
0

Lösung, um zu überprüfen, ob auch ein Wert vorhanden ist.

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

Dieser funktioniert für mich:

Arrays.asList(YourEnum.values()).toString().contains("valueToCheck");
EDiaz
quelle
3
Ihre Version gibt true zurück, auch wenn YourEnum "valueToCheckBlaBla" enthält, da "valueToCheck" in der Zeichenfolgendarstellung der gesamten Liste vorhanden ist.
Nicko
0

Wenn Sie Java 8 oder höher verwenden, können Sie dies tun:

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

Sie können es als enthält-Methode erstellen:

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

oder Sie können es einfach mit Ihrem Codeblock verwenden:

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

Sie können auch Folgendes verwenden: com.google.common.base.Enums

Enums.getIfPresent (varEnum.class, varToLookFor) gibt eine Option zurück

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

Rabhi Salim
quelle
0

Ich würde einfach schreiben,

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

Enum # equals funktioniert nur mit Objektvergleich.

Swadeshi
quelle
-1
public boolean contains(Choices value) {
   return EnumSet.allOf(Choices.class).contains(value);
}
Anton
quelle
das wird nicht funktionieren set haben Enum-Objekte, während Sie nach einer Zeichenfolge suchen.
iTake
Jetzt passt die Antwort nicht zu der Frage, da es sich um String handelte :)
iTake
-11

enumsind in Java ziemlich mächtig. Sie können containsIhrer Aufzählung ganz einfach eine Methode hinzufügen (wie Sie einer Klasse eine Methode hinzufügen würden):

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
quelle
Meinten Sie s.equals("b1") || s.equals("b2")?
Jigar Joshi
3
Dies ist wahrscheinlich nicht der beste Weg, da Sie s.equals("xx")für jede Aufzählung, die Sie später hinzufügen, neue hinzufügen müssen.
Jluzwick
1
Und es wird mehr als 1000 Aufzählungen geben.
Jared
20
Wie erhalten Menschen, die schreckliche Lösungen wie diese vorschlagen , einen 64K-Ruf? Ich hasse es, an all den beschissenen Code zu denken, der basierend auf den Antworten dieser Mitwirkenden herausgeschleudert wird
Dexygen