Maison >Tutoriel système >Linux >Comparaison des avantages et des inconvénients de plusieurs implémentations de recherche d'énumération Java qui ne génèrent pas d'exceptions
Présentation | Java Enum est une fonctionnalité très utile, mais de nombreuses personnes n'en profitent souvent pas pleinement car certaines bibliothèques ne donnent pas la priorité à cette fonctionnalité. Habituellement, nous pouvons également utiliser correctement la fonction d'énumération Java, mais il existe souvent un tel problème dans de nombreuses bases de code, c'est pourquoi cet article est écrit. La question est simple : comment obtenir une énumération par nom ou valeur et ignorer les valeurs inexistantes ? |
C'est l'énumération que nous utiliserons dans notre exemple. L'énumération la plus complexe est sélectionnée afin que l'énumération de recherche puisse également être représentée par d'autres champs.
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; }
Voir-le sur GitHub.
QuestionUtiliser Enum.valueOf est génial lorsque vous savez que l'entrée est valide. Cependant, si un nom non valide est transmis, une exception sera levée. Dans certains cas, c'est bien. En général, cependant, nous préférons ignorer l’exception et renvoyer null.
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)Mauvaise mise en œuvre
Malheureusement, les deux méthodes suivantes apparaissent très fréquemment dans la base de code. N’apprenez pas des exemples négatifs.
Enum.valueOf avec Try Catch (mauvais)Cette mauvaise pratique est plus courante chez les débutants. Les exceptions ne doivent pas être utilisées pour le flux de contrôle, et cela peut avoir un certain impact sur les performances. Ne soyez pas paresseux. Vous devez le faire de la bonne manière.
/* * 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 nullRechercher par itération (inférieur)
Cette méthode est également très courante (voir ici), mais au moins le programmeur sait que try/catch ne peut pas être utilisé pour intercepter des exceptions. Alors, quel est le problème avec cette approche ? C'est vrai, il parcourt toutes les énumérations jusqu'à ce qu'il trouve une énumération correspondante ou renvoie null - n fois dans le pire des cas, où n est le nombre de valeurs d'énumération. Certains pourraient penser qu’il s’agit d’une optimisation triviale et simplement prématurée. Cependant, les structures de données et les algorithmes constituent la base du CS. Il est beaucoup moins laborieux d'utiliser une Map au lieu de parcourir une collection. Est-ce que cela améliorera considérablement les performances ? Non, mais c'est une bonne habitude à prendre. Lors des entretiens avec des candidats, vous sentiriez-vous à l'aise avec un algorithme de recherche de complexité linéaire ? À ce stade, vous ne devriez pas laisser passer une telle révision de code.
/* * 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 nullMeilleure mise en œuvre
Les éléments suivants peuvent tous fonctionner en utilisant des index sous forme de carte. Il existe cependant quelques différences subtiles entre eux.
Index de carte statique (meilleur)Quelle est la structure de données correcte pour une recherche rapide de taille fixe ? C'est HashMap. Désormais, avec un passe-partout supplémentaire, nous pouvons effectuer des recherches plus efficaces à condition d'avoir une bonne fonction de hachage. Un peu plus verbeux, mais ce serait formidable s'il y avait un moyen de réduire le passe-partout.
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 (recommandé)
Il s'agit d'un cas d'utilisation courant et nos amis de Google ont une solution très propre et sans passe-partout. En regardant sous le capot, il utilise même WeakReferences et WeakHashMaps. Fondamentalement, ce code créera une carte statique globale saisie dans le nom de la classe Enum et l'utilisera pour les recherches.
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 nullAutre indexation par domaine
Cette même méthode peut être utilisée pour d'autres champs de l'énumération. Il n'est pas rare de vouloir rechercher une énumération par son nom affiché ou d'autres propriétés.
Carte statique indexée par champ (mieux)Même méthode que ci-dessus, mais indexation sur le nom d'affichage au lieu du nom d'énumération.
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 nullCarte statique indexée par champ (mieux)
Nous ne pouvons pas exploiter Guava ici car il serait difficile de créer une clé globale unique pour un index statique. Mais cela ne veut pas dire que nous n’avons pas d’aide !
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); } }
Nous disposons désormais d’une solution universelle qui n’a plus grand-chose à voir avec le passe-partout.
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 nullConclusion
Il existe ici plusieurs méthodes qui peuvent être utilisées pour résoudre le même problème. Certains sont mauvais, d’autres meilleurs.
Texte anglais original : recherche d'énumération Java par nom ou par champ sans exception
Auteur de la traduction : MaNong.com – Xiaofeng
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!