Überprüfen Sie, ob Enum in Java existiert

Lesezeit: 10 Minuten

Benutzer-Avatar
Danni

Gibt es eine Möglichkeit, zu überprüfen, ob eine Aufzählung existiert, indem sie mit einer bestimmten Zeichenfolge verglichen wird? Irgendwie finde ich keine solche Funktion. Ich könnte einfach versuchen, die zu verwenden valueOf -Methode verwenden und eine Ausnahme abfangen, aber mir wurde beigebracht, dass das Abfangen von Laufzeitausnahmen keine gute Praxis ist. Hat jemand Ideen?

  • Ich verstehe die Idee hinter emum valueOf das Auslösen einer Ausnahme wirklich nicht … es macht keinen Sinn. Es wäre in jeder Hinsicht viel praktischer, wenn es nur NULL zurückgeben würde.

    – Marcolopes

    6. Februar 2013 um 8:11 Uhr

  • @marcolopes: Ein Grund wäre, alle möglichen Fälle mit Enum abdecken zu wollen. Wenn ein Enum nicht gefunden wird, bedeutet dies, dass der Entwickler so schnell wie möglich benachrichtigt werden sollte, dass ein Fall fehlt. Es sollte nicht zulassen, dass das Programm später im Code einen NullPointerError an anderer Stelle auslöst.

    – Eric Duminil

    7. Februar 2019 um 11:23 Uhr


  • @EricDuminil ein Nullergebnis wäre einfacher … das mache ich! Ich verwende valueOf grundsätzlich nicht in meinem Code und schreibe eine neue Methode in die Aufzählung get(value), die die Ausnahme abfängt …

    – Marcolopes

    10. Februar 2019 um 21:20 Uhr


Benutzer-Avatar
Jon Skeet

Wenn ich dies tun muss, baue ich manchmal eine Set<String> der Namen, oder sogar meine eigenen Map<String,MyEnum> – dann können Sie das einfach überprüfen.

Ein paar erwähnenswerte Punkte:

  • Füllen Sie eine solche statische Sammlung in einem statischen Initialisierer auf. Nicht Verwenden Sie einen Variableninitialisierer und verlassen Sie sich dann darauf, dass er ausgeführt wurde, wenn der Enum-Konstruktor ausgeführt wird – das wird es nicht gewesen sein! (Die Aufzählungskonstruktoren sind die ersten Dinge, die vor dem statischen Initialisierer ausgeführt werden.)
  • Versuchen Sie, die Verwendung zu vermeiden values() häufig – es muss jedes Mal ein neues Array erstellt und gefüllt werden. Um über alle Elemente zu iterieren, verwenden Sie EnumSet.allOf was für Aufzählungen ohne eine große Anzahl von Elementen viel effizienter ist.

Beispielcode:

import java.util.*;

enum SampleEnum {
    Foo,
    Bar;

    private static final Map<String, SampleEnum> nameToValueMap =
        new HashMap<String, SampleEnum>();
    
    static {
        for (SampleEnum value : EnumSet.allOf(SampleEnum.class)) {
            nameToValueMap.put(value.name(), value);
        }
    }
    
    public static SampleEnum forName(String name) {
        return nameToValueMap.get(name);
    }
}

public class Test {
    public static void main(String [] args)
        throws Exception { // Just for simplicity!
        System.out.println(SampleEnum.forName("Foo"));
        System.out.println(SampleEnum.forName("Bar"));
        System.out.println(SampleEnum.forName("Baz"));
    }
}

Wenn Sie nur wenige Namen haben, ist dies natürlich wahrscheinlich übertrieben – eine O (n) -Lösung gewinnt oft über eine O (1) -Lösung, wenn n klein genug ist. Hier ist ein anderer Ansatz:

import java.util.*;

enum SampleEnum {
    Foo,
    Bar;

    // We know we'll never mutate this, so we can keep
    // a local copy.
    private static final SampleEnum[] copyOfValues = values();
    
    public static SampleEnum forName(String name) {
        for (SampleEnum value : copyOfValues) {
            if (value.name().equals(name)) {
                return value;
            }
        }
        return null;
    }
}

public class Test {
    public static void main(String [] args)
        throws Exception { // Just for simplicity!
        System.out.println(SampleEnum.forName("Foo"));
        System.out.println(SampleEnum.forName("Bar"));
        System.out.println(SampleEnum.forName("Baz"));
    }
}

  • values() erstellt und füllt ein neues Array? Ich kann mich nicht erinnern, das schon einmal gehört zu haben, aber vermutlich hast du eine Quelle?

    – Michael myers

    22. Juli 2009 um 20:27 Uhr

  • Nun, es gibt ein Array zurück … und es kann Sie nicht davor schützen, dieses Array zu mutieren … und nachfolgende Aufrufer würden dadurch nicht behindert. Ansonsten schau dir die JRE-Quellen an 🙂

    – Jon Skeet

    22. Juli 2009 um 20:29 Uhr

  • Ups, Entschuldigung – ja, habe gerade selbst nachgesehen. Dekompilieren Sie dann eine Aufzählung 🙂 Es verwendet clone (), was ziemlich schnell sein kann, aber nicht so schnell, als müsste man es überhaupt nicht tun …

    – Jon Skeet

    22. Juli 2009 um 20:32 Uhr

  • Gibt es Richtlinien, welche „Größe“-Aufzählungen vom kartenbasierten Ansatz im Vergleich zum Array/Loop-Ansatz profitieren? (Ich suche keine harten/schnellen Regeln, nur einen allgemeinen Anhaltspunkt dafür, wann man darüber nachdenken sollte, auf die eine oder andere Weise vorzugehen.)

    – cdeszaq

    12. Mai 2014 um 19:28 Uhr

  • @cdeszaq: Ich vermute, dass es zwischen JVMs und Situationen erheblich variieren kann. Wenn Sie befürchten, dass dies in Ihrer App von Bedeutung sein könnte, schlage ich vor, dass Sie Leistungstests durchführen. Tut mir leid, dass ich nicht hilfreicher sein kann 🙁

    – Jon Skeet

    12. Mai 2014 um 19:37 Uhr

Benutzer-Avatar
Michael myers

Ich glaube nicht, dass es eine integrierte Möglichkeit gibt, dies zu tun, ohne Ausnahmen abzufangen. Sie könnten stattdessen so etwas verwenden:

public static MyEnum asMyEnum(String str) {
    for (MyEnum me : MyEnum.values()) {
        if (me.name().equalsIgnoreCase(str))
            return me;
    }
    return null;
}

Bearbeiten: Wie Jon Skeet feststellt, values() funktioniert, indem bei jedem Aufruf ein privates Sicherungsarray geklont wird. Wenn die Leistung kritisch ist, möchten Sie vielleicht anrufen values() nur einmal das Array zwischenspeichern und es durchlaufen.

Wenn Ihre Aufzählung eine große Anzahl von Werten hat, ist die Map-Alternative von Jon Skeet wahrscheinlich besser als jede Array-Iteration.

Benutzer-Avatar
рüффп

Eine meiner Lieblingsbibliotheken: Apache Commons.

Das EnumUtils kann das problemlos.

Nach einem Beispiel zum Validieren einer Enum mit dieser Bibliothek:

public enum MyEnum {
    DIV("div"), DEPT("dept"), CLASS("class");

    private final String val;

    MyEnum(String val) {
    this.val = val;
    }

    public String getVal() {
    return val;
    }
}


MyEnum strTypeEnum = null;

// test if String str is compatible with the enum 
// e.g. if you pass str = "div", it will return false. If you pass "DIV", it will return true.
if( EnumUtils.isValidEnum(MyEnum.class, str) ){
    strTypeEnum = MyEnum.valueOf(str);
}

  • Ja genau. Apache Commons ist eine voll ausgestattete Küche; lohnt sich nur, wenn Sie den vollen oder maximalen Nutzen daraus ziehen 🙂 Square Wheel 😛

    – Mama

    7. Januar 2017 um 20:10 Uhr

  • Meiner Meinung nach muss Apache Commons eine “gemeinsame Basisabhängigkeit” sein, um sie in alle Java-Projekte aufzunehmen 😉

    – рüффп

    7. Januar 2017 um 20:12 Uhr

  • @DasariVinodh vielleicht ist es an der Zeit, Ihre Abhängigkeiten zu migrieren?

    – рüффп

    18. September 2018 um 16:25 Uhr

  • Diese Implementierung versucht intern, valueOf() zu fangen, was genau das OP zu vermeiden versucht. Ich weiß nicht, wie dies eine so positive Antwort ist.

    – Harogaston

    16. August 2019 um 19:34 Uhr


  • @рüффп OP sagt eindeutig “aber mir wurde beigebracht, dass das Abfangen von Laufzeitausnahmen keine gute Praxis ist”. Diese Implementierung tut genau das. Außerdem gibt es keine Möglichkeit, das Abfangen einer Ausnahme zu “optimieren”, es sei denn, Sie optimieren die JVM selbst. Das bleibt also nicht die richtige Antwort.

    – Harogaston

    11. September 2019 um 16:45 Uhr

Benutzer-Avatar
Nr

Ich weiß nicht, warum Ihnen jemand gesagt hat, dass das Abfangen von Laufzeitausnahmen schlecht ist.

Verwenden valueOf und fangen IllegalArgumentException ist gut zum Konvertieren/Überprüfen einer Zeichenfolge in eine Aufzählung.

Basierend auf der Antwort von Jon Skeet habe ich eine Klasse erstellt, die es ermöglicht, dies bei der Arbeit einfach zu tun:

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;

import java.util.EnumSet;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * <p>
 * This permits to easily implement a failsafe implementation of the enums's valueOf
 * Better use it inside the enum so that only one of this object instance exist for each enum...
 * (a cache could solve this if needed)
 * </p>
 *
 * <p>
 * Basic usage exemple on an enum class called MyEnum:
 *
 *   private static final FailSafeValueOf<MyEnum> FAIL_SAFE = FailSafeValueOf.create(MyEnum.class);
 *   public static MyEnum failSafeValueOf(String enumName) {
 *       return FAIL_SAFE.valueOf(enumName);
 *   }
 *
 * </p>
 *
 * <p>
 * You can also use it outside of the enum this way:
 *   FailSafeValueOf.create(MyEnum.class).valueOf("EnumName");
 * </p>
 *
 * @author Sebastien Lorber <i>([email protected])</i>
 */
public class FailSafeValueOf<T extends Enum<T>> {

    private final Map<String,T> nameToEnumMap;

    private FailSafeValueOf(Class<T> enumClass) {
        Map<String,T> map = Maps.newHashMap();
        for ( T value : EnumSet.allOf(enumClass)) {
            map.put( value.name() , value);
        }
        nameToEnumMap = ImmutableMap.copyOf(map);
    }

    /**
     * Returns the value of the given enum element
     * If the 
     * @param enumName
     * @return
     */
    public T valueOf(String enumName) {
        return nameToEnumMap.get(enumName);
    }

    public static <U extends Enum<U>> FailSafeValueOf<U> create(Class<U> enumClass) {
        return new FailSafeValueOf<U>(enumClass);
    }

}

Und der Unit-Test:

import org.testng.annotations.Test;

import static org.testng.Assert.*;


/**
 * @author Sebastien Lorber <i>([email protected])</i>
 */
public class FailSafeValueOfTest {

    private enum MyEnum {
        TOTO,
        TATA,
        ;

        private static final FailSafeValueOf<MyEnum> FAIL_SAFE = FailSafeValueOf.create(MyEnum.class);
        public static MyEnum failSafeValueOf(String enumName) {
            return FAIL_SAFE.valueOf(enumName);
        }
    }

    @Test
    public void testInEnum() {
        assertNotNull( MyEnum.failSafeValueOf("TOTO") );
        assertNotNull( MyEnum.failSafeValueOf("TATA") );
        assertNull( MyEnum.failSafeValueOf("TITI") );
    }

    @Test
    public void testInApp() {
        assertNotNull( FailSafeValueOf.create(MyEnum.class).valueOf("TOTO") );
        assertNotNull( FailSafeValueOf.create(MyEnum.class).valueOf("TATA") );
        assertNull( FailSafeValueOf.create(MyEnum.class).valueOf("TITI") );
    }

}

Beachten Sie, dass ich Guava verwendet habe, um eine ImmutableMap zu erstellen, aber eigentlich könnten Sie eine normale Karte verwenden, denke ich, da die Karte nie zurückgegeben wird …

Benutzer-Avatar
Alexander Daum

Die meisten Antworten schlagen vor, entweder eine Schleife mit equals zu verwenden, um zu prüfen, ob die Aufzählung existiert, oder try/catch mit enum.valueOf() zu verwenden. Ich wollte wissen, welche Methode schneller ist und habe es ausprobiert. Ich bin nicht sehr gut im Benchmarking, also korrigiert mich bitte, wenn ich Fehler gemacht habe.

Heres der Code meiner Hauptklasse:

    package enumtest;

public class TestMain {

    static long timeCatch, timeIterate;
    static String checkFor;
    static int corrects;

    public static void main(String[] args) {
        timeCatch = 0;
        timeIterate = 0;
        TestingEnum[] enumVals = TestingEnum.values();
        String[] testingStrings = new String[enumVals.length * 5];
        for (int j = 0; j < 10000; j++) {
            for (int i = 0; i < testingStrings.length; i++) {
                if (i % 5 == 0) {
                    testingStrings[i] = enumVals[i / 5].toString();
                } else {
                    testingStrings[i] = "DOES_NOT_EXIST" + i;
                }
            }

            for (String s : testingStrings) {
                checkFor = s;
                if (tryCatch()) {
                    ++corrects;
                }
                if (iterate()) {
                    ++corrects;
                }
            }
        }

        System.out.println(timeCatch / 1000 + "us for try catch");
        System.out.println(timeIterate / 1000 + "us for iterate");
        System.out.println(corrects);
    }

    static boolean tryCatch() {
        long timeStart, timeEnd;
        timeStart = System.nanoTime();
        try {
            TestingEnum.valueOf(checkFor);
            return true;
        } catch (IllegalArgumentException e) {
            return false;
        } finally {
            timeEnd = System.nanoTime();
            timeCatch += timeEnd - timeStart;
        }

    }

    static boolean iterate() {
        long timeStart, timeEnd;
        timeStart = System.nanoTime();
        TestingEnum[] values = TestingEnum.values();
        for (TestingEnum v : values) {
            if (v.toString().equals(checkFor)) {
                timeEnd = System.nanoTime();
                timeIterate += timeEnd - timeStart;
                return true;
            }
        }
        timeEnd = System.nanoTime();
        timeIterate += timeEnd - timeStart;
        return false;
    }
}

Das bedeutet, dass jede Methode das 50000-fache der Länge der Aufzählung ausgeführt wird. Ich habe diesen Test mehrmals mit 10, 20, 50 und 100 Aufzählungskonstanten ausgeführt. Hier sind die Ergebnisse:

  • 10: versuchen/fangen: 760 ms | Iteration: 62 ms
  • 20: Versuch/Fang: 1671 ms | Iteration: 177 ms
  • 50: Versuch/Fang: 3113 ms | Iteration: 488 ms
  • 100: Versuch/Fang: 6834 ms | Iteration: 1760 ms

Diese Ergebnisse waren nicht exakt. Bei erneuter Ausführung gibt es bis zu 10% Unterschied in den Ergebnissen, aber sie reichen aus, um zu zeigen, dass die Try/Catch-Methode weitaus weniger effizient ist, insbesondere bei kleinen Enums.

Benutzer-Avatar
Magnilex

Seit Java 8 konnten wir verwenden Ströme statt for-Schleifen. Es kann auch angebracht sein, ein zurückzugeben Optional wenn die Aufzählung keine Instanz mit einem solchen Namen hat.

Ich habe mir die folgenden drei Alternativen zum Nachschlagen einer Aufzählung ausgedacht:

private enum Test {
    TEST1, TEST2;

    public Test fromNameOrThrowException(String name) {
        return Arrays.stream(values())
                .filter(e -> e.name().equals(name))
                .findFirst()
                .orElseThrow(() -> new IllegalArgumentException("No enum with name " + name));
    }

    public Test fromNameOrNull(String name) {
        return Arrays.stream(values()).filter(e -> e.name().equals(name)).findFirst().orElse(null);
    }

    public Optional<Test> fromName(String name) {
        return Arrays.stream(values()).filter(e -> e.name().equals(name)).findFirst();
    }
}

1181640cookie-checkÜberprüfen Sie, ob Enum in Java existiert

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy