Heim > Artikel > System-Tutorial > Vergleich der Vor- und Nachteile mehrerer Java-Enumerations-Lookup-Implementierungen, die keine Ausnahmen auslösen
Einführung | Java Enum ist eine sehr nützliche Funktion, aber viele Leute nutzen sie oft nicht in vollem Umfang, da einige Bibliotheken dieser Funktion keine Priorität einräumen. Normalerweise können wir die Java-Aufzählungsfunktion auch korrekt verwenden, aber in vielen Codebasen gibt es häufig ein solches Problem, weshalb dieser Artikel geschrieben wurde. Die Frage ist einfach: Wie sollen wir eine Enumeration nach Name oder Wert erhalten und nicht vorhandene Werte ignorieren? |
Dies ist die Aufzählung, die wir in unserem Beispiel verwenden werden. Die komplexere Aufzählung wird ausgewählt, damit die Nachschlageaufzählung auch durch andere Felder dargestellt werden kann.
public enum CardColor { RED, BLACK, ; } // Jackson annotation to print the enum as an Object instead of the default name. @JsonFormat(shape = JsonFormat.Shape.OBJECT) public enum CardSuit { // Unicode suits - https://en.wikipedia.org/wiki/Playing_cards_in_Unicode SPADE("Spade", String.valueOf((char) 0x2660), CardColor.BLACK), HEART("Heart", String.valueOf((char) 0x2665), CardColor.RED), DIAMOND("Diamond", String.valueOf((char) 0x2666), CardColor.RED), CLUB("Club", String.valueOf((char) 0x2663), CardColor.BLACK), ; private String displayName; private String symbol; private CardColor color; private CardSuit(String displayName, String symbol, CardColor color) { this.displayName = displayName; this.symbol = symbol; this.color = color; } public String getDisplayName() { return displayName; } public void setDisplayName(String displayName) { this.displayName = displayName; } public String getSymbol() { return symbol; } public void setSymbol(String symbol) { this.symbol = symbol; } public CardColor getColor() { return color; } public void setColor(CardColor color) { this.color = color; }
Sehen Sie es sich auf GitHub an.
FrageDie Verwendung von Enum.valueOf ist großartig, wenn Sie wissen, dass die Eingabe gültig ist. Wenn jedoch ein ungültiger Name übergeben wird, wird eine Ausnahme ausgelöst. In manchen Fällen ist das in Ordnung. Im Allgemeinen ignorieren wir die Ausnahme jedoch lieber und geben null zurück.
log.debug("Running valueOf"); for (String name : names) { try { log.debug("looking up {} found {}", name, Json.serializer().toString(CardSuit.valueOf(name))); } catch (Exception ex) { log.warn("Exception Thrown", ex); } }
2017-02-22 14:46:38.556 [main] DEBUG c.s.examples.common.EnumLookup - Running valueOf 2017-02-22 14:46:38.804 [main] DEBUG c.s.examples.common.EnumLookup - looking up SPADE found {"displayName":"Spade","symbol":"♠","color":"BLACK"} 2017-02-22 14:46:38.806 [main] DEBUG c.s.examples.common.EnumLookup - looking up HEART found {"displayName":"Heart","symbol":"♥","color":"RED"} 2017-02-22 14:46:38.806 [main] DEBUG c.s.examples.common.EnumLookup - looking up DIAMOND found {"displayName":"Diamond","symbol":"♦","color":"RED"} 2017-02-22 14:46:38.806 [main] DEBUG c.s.examples.common.EnumLookup - looking up CLUB found {"displayName":"Club","symbol":"♣","color":"BLACK"} 2017-02-22 14:46:38.808 [main] WARN c.s.examples.common.EnumLookup - Exception Thrown java.lang.IllegalArgumentException: No enum constant com.stubbornjava.examples.common.EnumLookup.CardSuit.Missing at java.lang.Enum.valueOf(Enum.java:238) at com.stubbornjava.examples.common.EnumLookup$CardSuit.valueOf(EnumLookup.java:1) at com.stubbornjava.examples.common.EnumLookup.main(EnumLookup.java:154)Schlechte Umsetzung
Leider kommen die folgenden beiden Methoden so häufig in der Codebasis vor. Lernen Sie nicht aus negativen Beispielen.
Enum.valueOf mit Try Catch (schlecht)Diese schlechte Praxis kommt am häufigsten bei Anfängern vor. Ausnahmen sollten nicht für die Ablaufsteuerung verwendet werden, da dies möglicherweise Auswirkungen auf die Leistung haben kann. Seien Sie nicht faul. Man muss es richtig machen.
/* * Please don't do this! Using try / catch for * control flow is a bad practice. */ public static CardSuit trycatchValueOf(String name) { try { return CardSuit.valueOf(name); } catch (Exception ex) { log.warn("Exception Thrown", ex); return null; } }
log.debug("Running trycatchValueOf"); for (String name : names) { log.debug("looking up {} found {}", name, Json.serializer().toString(CardSuit.trycatchValueOf(name))); }
2017-02-22 14:46:38.809 [main] DEBUG c.s.examples.common.EnumLookup - Running trycatchValueOf 2017-02-22 14:46:38.809 [main] DEBUG c.s.examples.common.EnumLookup - looking up SPADE found {"displayName":"Spade","symbol":"♠","color":"BLACK"} 2017-02-22 14:46:38.809 [main] DEBUG c.s.examples.common.EnumLookup - looking up HEART found {"displayName":"Heart","symbol":"♥","color":"RED"} 2017-02-22 14:46:38.809 [main] DEBUG c.s.examples.common.EnumLookup - looking up DIAMOND found {"displayName":"Diamond","symbol":"♦","color":"RED"} 2017-02-22 14:46:38.809 [main] DEBUG c.s.examples.common.EnumLookup - looking up CLUB found {"displayName":"Club","symbol":"♣","color":"BLACK"} 2017-02-22 14:46:38.809 [main] WARN c.s.examples.common.EnumLookup - Exception Thrown java.lang.IllegalArgumentException: No enum constant com.stubbornjava.examples.common.EnumLookup.CardSuit.Missing at java.lang.Enum.valueOf(Enum.java:238) at com.stubbornjava.examples.common.EnumLookup$CardSuit.valueOf(EnumLookup.java:1) at com.stubbornjava.examples.common.EnumLookup$CardSuit.trycatchValueOf(EnumLookup.java:89) at com.stubbornjava.examples.common.EnumLookup.main(EnumLookup.java:171) 2017-02-22 14:46:38.809 [main] DEBUG c.s.examples.common.EnumLookup - looking up Missing found nullSuchen nach Iteration (minderwertig)
Diese Methode ist auch sehr verbreitet (siehe hier), aber zumindest weiß der Programmierer, dass try/catch nicht zum Abfangen von Ausnahmen verwendet werden kann. Was ist also falsch an diesem Ansatz? Das ist richtig, es durchläuft alle Aufzählungen, bis es eine passende Aufzählung findet oder null zurückgibt – im schlimmsten Fall n-mal, wobei n die Anzahl der Aufzählungswerte ist. Manche denken vielleicht, dass dies eine triviale und einfach verfrühte Optimierung ist. Datenstrukturen und Algorithmen sind jedoch die Grundlage von CS. Es ist viel weniger aufwändig, eine Map zu verwenden, anstatt eine Sammlung zu durchlaufen. Wird dies die Leistung erheblich verbessern? Nein, aber es ist eine gute Angewohnheit, sich anzueignen. Würden Sie sich bei Bewerbungsgesprächen mit einem linearen Komplexitätssuchalgorithmus wohl fühlen? An dieser Stelle sollten Sie eine solche Codeüberprüfung nicht verstreichen lassen.
/* * Please don't do this! It is inefficient and it's * not very hard to use Guava or a static Map as an index. */ public static CardSuit iterationFindByName(String name) { for (CardSuit suit : CardSuit.values()) { if (name.equals(suit.name())) { return suit; } } return null; }
log.debug("Running iteration"); for (String name : names) { log.debug("looking up {} found {}", name, Json.serializer().toString(CardSuit.iterationFindByName(name))); }
2017-02-22 14:46:38.808 [main] DEBUG c.s.examples.common.EnumLookup - Running iteration 2017-02-22 14:46:38.809 [main] DEBUG c.s.examples.common.EnumLookup - looking up SPADE found {"displayName":"Spade","symbol":"♠","color":"BLACK"} 2017-02-22 14:46:38.809 [main] DEBUG c.s.examples.common.EnumLookup - looking up HEART found {"displayName":"Heart","symbol":"♥","color":"RED"} 2017-02-22 14:46:38.809 [main] DEBUG c.s.examples.common.EnumLookup - looking up DIAMOND found {"displayName":"Diamond","symbol":"♦","color":"RED"} 2017-02-22 14:46:38.809 [main] DEBUG c.s.examples.common.EnumLookup - looking up CLUB found {"displayName":"Club","symbol":"♣","color":"BLACK"} 2017-02-22 14:46:38.809 [main] DEBUG c.s.examples.common.EnumLookup - looking up Missing found nullBessere Umsetzung
Das Folgende kann durch die Verwendung von Indizes in Kartenform funktionieren. Es gibt jedoch einige subtile Unterschiede zwischen ihnen.
Statischer Kartenindex (besser)Was ist die richtige Datenstruktur für eine schnelle Suche mit fester Größe? Das ist HashMap. Mit etwas mehr Boilerplate können wir jetzt effizientere Suchvorgänge durchführen, vorausgesetzt, wir haben eine gute Hash-Funktion. Etwas ausführlicher, aber es wäre toll, wenn es eine Möglichkeit gäbe, den Boilerplate zu reduzieren.
private static final Map<String, CardSuit> nameIndex = Maps.newHashMapWithExpectedSize(CardSuit.values().length); static { for (CardSuit suit : CardSuit.values()) { nameIndex.put(suit.name(), suit); } } public static CardSuit lookupByName(String name) { return nameIndex.get(name); }
log.debug("Running lookupByName"); for (String name : names) { log.debug("looking up {} found {}", name, Json.serializer().toString(CardSuit.lookupByName(name))); }
2017-02-22 14:46:38.809 [main] DEBUG c.s.examples.common.EnumLookup - Running lookupByName 2017-02-22 14:46:38.809 [main] DEBUG c.s.examples.common.EnumLookup - looking up SPADE found {"displayName":"Spade","symbol":"♠","color":"BLACK"} 2017-02-22 14:46:38.810 [main] DEBUG c.s.examples.common.EnumLookup - looking up HEART found {"displayName":"Heart","symbol":"♥","color":"RED"} 2017-02-22 14:46:38.810 [main] DEBUG c.s.examples.common.EnumLookup - looking up DIAMOND found {"displayName":"Diamond","symbol":"♦","color":"RED"} 2017-02-22 14:46:38.813 [main] DEBUG c.s.examples.common.EnumLookup - looking up CLUB found {"displayName":"Club","symbol":"♣","color":"BLACK"} 2017-02-22 14:46:38.813 [main] DEBUG c.s.examples.common.EnumLookup - looking up Missing found nullGuava Enums.getIfPresent (empfohlen)
Dies ist ein häufiger Anwendungsfall und unsere Freunde bei Google haben dafür eine sehr saubere und einfache Lösung. Unter der Haube werden sogar WeakReferences und WeakHashMaps verwendet. Im Grunde erstellt dieser Code eine globale statische Karte, die im Enum-Klassennamen eingegeben wird, und verwendet sie für Suchvorgänge.
public static CardSuit getIfPresent(String name) { return Enums.getIfPresent(CardSuit.class, name).orNull(); }
log.debug("Running Guava getIfPresent"); for (String name : names) { log.debug("looking up {} found {}", name, Json.serializer().toString(CardSuit.getIfPresent(name))); }
2017-02-22 14:46:38.813 [main] DEBUG c.s.examples.common.EnumLookup - Running Guava getIfPresent 2017-02-22 14:46:38.814 [main] DEBUG c.s.examples.common.EnumLookup - looking up SPADE found {"displayName":"Spade","symbol":"♠","color":"BLACK"} 2017-02-22 14:46:38.814 [main] DEBUG c.s.examples.common.EnumLookup - looking up HEART found {"displayName":"Heart","symbol":"♥","color":"RED"} 2017-02-22 14:46:38.815 [main] DEBUG c.s.examples.common.EnumLookup - looking up DIAMOND found {"displayName":"Diamond","symbol":"♦","color":"RED"} 2017-02-22 14:46:38.815 [main] DEBUG c.s.examples.common.EnumLookup - looking up CLUB found {"displayName":"Club","symbol":"♣","color":"BLACK"} 2017-02-22 14:46:38.815 [main] DEBUG c.s.examples.common.EnumLookup - looking up Missing found nullWeitere Indizierung nach Feld
Diese exakt gleiche Methode kann für andere Felder der Enumeration verwendet werden. Es ist nicht ungewöhnlich, eine Aufzählung anhand ihres angezeigten Namens oder anderer Eigenschaften zu finden.
Statische Karte, indexiert nach Feld (besser)Gleiche Methode wie oben, aber Indizierung anhand des Anzeigenamens anstelle des Enumerationsnamens.
private static final Map<String, CardSuit> displayNameIndex = Maps.newHashMapWithExpectedSize(CardSuit.values().length); static { for (CardSuit suit : CardSuit.values()) { displayNameIndex.put(suit.getDisplayName(), suit); } } public static CardSuit lookupByDisplayName(String name) { return displayNameIndex.get(name); }
log.debug("Running lookupByDisplayName"); for (String displayName : displayNames) { log.debug("looking up {} found {}", displayName, Json.serializer().toString(CardSuit.lookupByDisplayName(displayName))); }
2017-02-22 14:46:38.815 [main] DEBUG c.s.examples.common.EnumLookup - Running lookupByDisplayName 2017-02-22 14:46:38.815 [main] DEBUG c.s.examples.common.EnumLookup - looking up Spade found {"displayName":"Spade","symbol":"♠","color":"BLACK"} 2017-02-22 14:46:38.815 [main] DEBUG c.s.examples.common.EnumLookup - looking up Heart found {"displayName":"Heart","symbol":"♥","color":"RED"} 2017-02-22 14:46:38.815 [main] DEBUG c.s.examples.common.EnumLookup - looking up Diamond found {"displayName":"Diamond","symbol":"♦","color":"RED"} 2017-02-22 14:46:38.816 [main] DEBUG c.s.examples.common.EnumLookup - looking up Club found {"displayName":"Club","symbol":"♣","color":"BLACK"} 2017-02-22 14:46:38.816 [main] DEBUG c.s.examples.common.EnumLookup - looking up Missing found nullStatische Karte, indexiert nach Feld (besser)
Wir können Guava hier nicht nutzen, da es schwierig wäre, einen eindeutigen globalen Schlüssel für einen statischen Index zu erstellen. Das heißt aber nicht, dass wir keine Hilfe haben!
public class EnumUtils { public static <T, E extends Enum<E>> Function<T, E> lookupMap(Class<E> clazz, Function<E, T> mapper) { @SuppressWarnings("unchecked") E[] emptyArray = (E[]) Array.newInstance(clazz, 0); return lookupMap(EnumSet.allOf(clazz).toArray(emptyArray), mapper); } public static <T, E extends Enum<E>> Function<T, E> lookupMap(E[] values, Function<E, T> mapper) { Map<T, E> index = Maps.newHashMapWithExpectedSize(values.length); for (E value : values) { index.put(mapper.apply(value), value); } return (T key) -> index.get(key); } }
Jetzt haben wir eine universelle Lösung, die wenig mit Boilerplate zu tun hat.
private static final Function<String, CardSuit> func = EnumUtils.lookupMap(CardSuit.class, e -> e.getDisplayName()); public static CardSuit lookupByDisplayNameUtil(String name) { return func.apply(name); }
log.debug("Running lookupByDisplayNameUtil"); for (String displayName : displayNames) { log.debug("looking up {} found {}", displayName, Json.serializer().toString(CardSuit.lookupByDisplayNameUtil(displayName))); }
2017-02-22 14:46:38.816 [main] DEBUG c.s.examples.common.EnumLookup - Running lookupByDisplayNameUtil 2017-02-22 14:46:38.816 [main] DEBUG c.s.examples.common.EnumLookup - looking up Spade found {"displayName":"Spade","symbol":"♠","color":"BLACK"} 2017-02-22 14:46:38.816 [main] DEBUG c.s.examples.common.EnumLookup - looking up Heart found {"displayName":"Heart","symbol":"♥","color":"RED"} 2017-02-22 14:46:38.816 [main] DEBUG c.s.examples.common.EnumLookup - looking up Diamond found {"displayName":"Diamond","symbol":"♦","color":"RED"} 2017-02-22 14:46:38.816 [main] DEBUG c.s.examples.common.EnumLookup - looking up Club found {"displayName":"Club","symbol":"♣","color":"BLACK"} 2017-02-22 14:46:38.816 [main] DEBUG c.s.examples.common.EnumLookup - looking up Missing found nullFazit
Hier gibt es mehrere Methoden, mit denen sich das gleiche Problem lösen lässt. Manche sind schlecht, manche sind besser.
Englischer Originaltext: Java-Enum-Suche nach Name oder Feld ohne Auslösen von Ausnahmen
Übersetzungsautor: MaNong.com – Xiaofeng
Das obige ist der detaillierte Inhalt vonVergleich der Vor- und Nachteile mehrerer Java-Enumerations-Lookup-Implementierungen, die keine Ausnahmen auslösen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!