Java: vérifier si enum contient une chaîne donnée?

169

Voici mon problème - je cherche (s'il existe) l'équivalent enum de ArrayList.contains();.

Voici un exemple de mon problème de code:

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

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

Maintenant, je me rends compte qu'un ArrayListof Stringsserait la meilleure route ici, mais je dois exécuter mon contenu enum via un commutateur / boîtier ailleurs. D'où mon problème.

En supposant que quelque chose comme ça n'existe pas, comment pourrais-je le faire?

Jared
la source
Le commutateur / boîtier avec chaînes est implémenté à partir de Java 7
AndreyP

Réponses:

206

Cela devrait le faire:

public static boolean contains(String test) {

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

    return false;
}

De cette façon, vous n'avez pas à vous soucier d'ajouter des valeurs d'énumération supplémentaires plus tard, elles sont toutes vérifiées.

Edit: Si l'énumération est très grande, vous pouvez coller les valeurs dans un HashSet:

public static HashSet<String> getEnums() {

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

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

  return values;
}

Ensuite, vous pouvez simplement faire: values.contains("your string")qui renvoie vrai ou faux.

Richard H
la source
12
c'est une implication très médiocre: Choice.valueOf (test) est ce que vous voulez (w / try / catch)
bestsss
17
bestsss, c'est clairement la solution la plus appropriée. Lancer une exception pour implémenter un type de méthode exist () est une mauvaise pratique. Bien que vous puissiez penser que votre implémentation est plus efficace car elle ne recherche pas O (n), c'est dans le cadre sous-jacent qui n'est pas visible. L'utilisation de try {} catch ajoute également une surcharge. De plus, ce n'est tout simplement pas joli.
jluzwick
25
@Jared, certainement valueOf. Attrapez simplement votre exception et renvoyez false. Pour ceux qui disent le contraire, si vous regardez l'implémentation, elle utilise déjà une carte, et les développeurs JDK ont une bien meilleure chance d'optimiser cela. L'API lève une exception, ce qui est une pratique discutable (au lieu de renvoyer null), mais lorsque vous traitez avec une API qui lève une exception, allez-y, ne réinventez pas la roue.
Yishai
2
@jluzwick try / catch overhead est une simple instruction de saut lorsqu'elle n'est pas prise, ne pas utiliser l'exception dans le bloc catch est également optimisé. La peur d'essayer / attraper la cause d'une perte de performance est une mauvaise pratique.
bestsss
23
contains () doit être préféré à valueOf () avec une exception. Pourquoi? Parce que "les exceptions sont, comme leur nom l'indique, à n'être utilisées que pour des conditions exceptionnelles; elles ne doivent jamais être utilisées pour un flux de contrôle ordinaire" (Joshua Bloch, "Effective Java").
james.garriss
226

Utilisez la librairie Apache commons lang3 à la place

 EnumUtils.isValidEnum(MyEnum.class, myValue)
RaphC
la source
33
Remarque pour les intéressés, l'implémentation sous-jacente qu'ils ont utilisée est simplement la solution try / catch (@since 3.0 @version $ Id: EnumUtils.java 1199894 2011-11-09 17: 53: 59Z ggregory $).
Jonathan Gawrych
1
Rend mon code plus simple, donc je m'en fiche s'il utilise une exception pour le contrôle de flux (ils ne devraient vraiment pas) ... Ce serait bien s'ils changeaient cela.
jpangamarca
1
La goyave contient-elle également une solution comme celle-ci?
Cypress Frankenfeld
50

Vous pouvez utiliser 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
  }
}

Si vous vous attendez à ce que la vérification échoue souvent, vous feriez peut-être mieux d'utiliser une simple boucle comme d'autres l'ont montré - si vos enums contiennent de nombreuses valeurs, peut-être builda HashSetou similaire de vos valeurs d'énumération converties en une chaîne et interrogez-la à la HashSetplace.

nos
la source
8
Je ne pense pas qu'Exception soit le meilleur choix dans un tel cas.
GokcenG
6
Try and Catch devrait être le dernier recours. Try and Catch est
trop
2
s'appuyer sur des exceptions d'exécution pour faire de la logique métier, en plus d'être coûteux, n'est pas aussi lisible. en ce qui concerne les exceptions vérifiées, c'est différent, car elles font partie de l'entreprise.
Luís Soares
2
Cela empêche également quiconque d'activer une pause large sur les exceptions lancées pour trouver des cas exceptionnels réels qui sont retentés. (ou du moins le rend très ennuyeux). Utilisez des exceptions pour des cas exceptionnels.
Nickolay Kondratyev
4
EnumUtils.isValidEnum(MyEnum.class, myValue)utilise une logique similaire et IMO, il est logique d'ajouter une bibliothèque entière pour cette tâche triviale
Vikramjit Roy
37

Si vous utilisez Java 1.8, vous pouvez choisir Stream + Lambda pour implémenter ceci:

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
la source
20

Guavas Enums pourrait être votre ami

Comme par exemple ceci:

enum MyData {
    ONE,
    TWO
}

@Test
public void test() {

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

Encore mieux:

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

};
Bansal dévash
la source
Merci pour la solution optimiste
Parth Patel
11

Vous pouvez d'abord convertir l'énumération en liste, puis utiliser la méthode list contains

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
la source
Cela devrait être la réponse acceptée. La conversion en liste est le moyen le plus propre de le faire. Cela évite toute la discussion (latérale) sur «l'utilisation correcte des exceptions» en Java dans les autres réponses ici.
Manuel
Lancez IllegalArgumentException si la valeur n'existe pas.
mkyong le
10

Quelques hypothèses:
1) Pas de try / catch, car il s'agit d'un contrôle de flux exceptionnel
2) La méthode «contient» doit être rapide, car elle s'exécute généralement plusieurs fois.
3) L'espace n'est pas limité (courant pour les solutions ordinaires)

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
la source
10

Quelques bibliothèques ont été mentionnées ici, mais celle que je recherchais me manque: le printemps!

Il y a le ObjectUtils # containsConstant qui est insensible à la casse par défaut, mais peut être strict si vous le souhaitez. Il est utilisé comme ceci:

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

Remarque: j'ai utilisé la méthode surchargée ici pour montrer comment utiliser la vérification sensible à la casse. Vous pouvez omettre le booléen pour avoir un comportement insensible à la casse.

Soyez prudent avec les grandes énumérations, car elles n'utilisent pas l'implémentation de Map comme certains le font ...

En prime, il fournit également une variante insensible à la casse de valueOf: ObjectUtils # caseInsensitiveValueOf

Pim Hazebroek
la source
7

Vous pouvez utiliser ceci

YourEnum {A1, A2, B1, B2}

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

La mise à jour suggérée par @ wightwulf1944 est intégrée pour rendre la solution plus efficace.

greperror
la source
4
Cette mise en œuvre est inefficace. Cela parcourt les valeurs d'énumération pour créer un nouvel ensemble, puis crée un flux, qui itère dans l'ensemble résultant. Cela signifie qu'un nouvel ensemble et un nouveau flux sont créés chaque fois que la fonction est appelée, et l'utilisation stream()sur un ensemble signifie que vous itérez sur chaque élément de l'ensemble au lieu de profiter de la table de hachage sous-jacente qui sera plus rapide. Pour améliorer cela, il est préférable de mettre en cache l'ensemble créé et d'utiliser sa contains()méthode à la place. Si vous devez obtenir un flux, utilisez à la Arrays.stream()place.
Subaru Tashiro
3

Je ne pense pas que ce soit le cas, mais vous pouvez faire quelque chose comme ceci:

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

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

Éditer:

Veuillez consulter la version de Richard car elle est plus appropriée car cela ne fonctionnera pas à moins que vous ne la convertissiez pour utiliser des chaînes, ce que fait Richards.

jluzwick
la source
mais je pense que l'OP veut tester une chaîne?
Richard H
Oui haha. Cette méthode ne serait pas aussi efficace car nous savons déjà que le choix est dans les énumérations. Votre modification est plus correcte.
jluzwick
1
Si vous souhaitez travailler sur un sous-ensemble des énumérations elles-mêmes (et non sur leurs noms), mieux vaut regarder EnumSet. download.oracle.com/javase/6/docs/api/java/util/EnumSet.html
Yishai
Peut-être une question stupide, mais pourquoi n'est-ce pas .values()documenté sur download.oracle.com/javase/6/docs/api/java/lang/Enum.html ?
Anonym
C'est une excellente question. Vous avez raison, il n'existe pas dans la documentation et il n'existe pas non plus dans la source Enum. Je suppose qu'il est présent dans l'une des implémentations d'Enum ou qu'il existe une règle JLS qui le permet. De plus, tous les objets Collection ont ceci, et cela peut être considéré comme une Collection même si elle n'implémente pas nécessairement Collection.
jluzwick
3

Java Streams offre un moyen élégant de le faire

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

Renvoie: vrai si des éléments du flux correspondent à la valeur fournie, sinon faux

Sourabh
la source
2

Pourquoi ne pas combiner la réponse de Pablo avec 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;
        }
}
Karthik. V
la source
Veuillez ne pas le faire. Voir une autre réponse plus ancienne qui équivaut à la vôtre: stackoverflow.com/a/4936872/103412
Torsten
1

Cette approche peut être utilisée pour vérifier n'importe quel Enum, vous pouvez l'ajouter à une 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;
}

Utilisez-le de cette façon:

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

J'ai créé la classe suivante pour cette validation

public class EnumUtils {

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

}

exemple d'utilisation:

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
la source
0

Vous pouvez utiliser valueOf("a1")si vous souhaitez rechercher par chaîne

Amir Afghani
la source
3
mais ce n'est pas élégant .. si la valeur n'existe pas, elle lève une exception. donc vous devez probablement l'entourer de try catch
Kasturi
Cela lèvera une exception si la valeur n'existe pas
Richard H
Moins élégant que de parcourir les choix d'énumération à la recherche d'un objet correspondant?
jprete
0

C'est une énumération, ce sont des valeurs constantes, donc si c'est dans une instruction switch, il fait juste quelque chose comme ceci:

case: val1
case: val2

Aussi, pourquoi auriez-vous besoin de savoir ce qui est déclaré comme constante?

Woot4Moo
la source
Ce morceau de code n'est pas dans ladite instruction switch, c'est ailleurs. Je disais simplement que dans ce cas, enum est nécessaire car d'autres avaient suggéré que j'utilise une ArrayList à la place.
Jared
@Jared, cela a beaucoup plus de sens maintenant
Woot4Moo
@Jared cependant, cela n'a pas vraiment d'importance car vous connaissez déjà les valeurs qui y sont. Fondamentalement, l'équivalent enum de list.contains () est MyEnum.MyAwesomeValue
Woot4Moo
0

Avec la goyave, c'est encore plus simple:

boolean isPartOfMyEnum(String myString){

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

}
chaiyachaiya
la source
Comme l'a dit Kioria , cela ne fonctionnera pas. MyEnum.values()renvoie un tableau d'instances MyEnum et MyEnum.value().toString()retourne une représentation sous forme de chaîne de cet objet tableau (juste une chaîne comme "[LMyEnum; @ 15b94ed3")
user2137020
Vous devez appeler .name()au lieu de .toString()(sauf si vous remplacez la méthode toString par défaut). Voir ceci pour plus d'informations: Différence entre enum .name () et .toString ()
Gaʀʀʏ
0

Cela combine toutes les approches des méthodes précédentes et devrait avoir des performances équivalentes. Il peut être utilisé pour n'importe quelle énumération, en ligne avec la solution "Edit" de @Richard H, et utilise des exceptions pour les valeurs invalides comme @bestsss. Le seul compromis est que la classe doit être spécifiée, mais cela transforme cela en deux lignes.

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

solution pour vérifier si la valeur est présente et obtenir la valeur enum en retour:

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

Celui-ci fonctionne pour moi:

Arrays.asList(YourEnum.values()).toString().contains("valueToCheck");
EDiaz
la source
3
Votre version retournera true même si YourEnum contient "valueToCheckBlaBla", car "valueToCheck" sera présent dans la représentation sous forme de chaîne de la liste entière.
Nicko
0

Si vous utilisez Java 8 ou supérieur, vous pouvez le faire:

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

Vous pouvez en faire une méthode contient:

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

ou vous pouvez simplement l'utiliser avec votre bloc de code:

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

vous pouvez également utiliser: com.google.common.base.Enums

Enums.getIfPresent (varEnum.class, varToLookFor) renvoie une option

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

Rabhi salim
la source
0

J'écrirais juste

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

Enum # equals ne fonctionne qu'avec la comparaison d'objets.

Swadeshi
la source
-1
public boolean contains(Choices value) {
   return EnumSet.allOf(Choices.class).contains(value);
}
Anton
la source
cela ne fonctionnera pas. set ont des objets enum, pendant que vous le vérifiez pour une chaîne.
iTake
maintenant, la réponse ne correspond pas à la question, car il s'agissait de String :)
iTake
-11

enumsont assez puissants en Java. Vous pouvez facilement ajouter une containsméthode à votre énumération (comme vous ajouteriez une méthode à une 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;
  } 

};
Pablo Santa Cruz
la source
voulez-vous dire s.equals("b1") || s.equals("b2")??
Jigar Joshi
3
Ce ne sera probablement pas la meilleure façon de le faire car vous devrez en ajouter un nouveau s.equals("xx")pour chaque énumération que vous ajoutez plus tard.
jluzwick
1
Et il y aura plus de 1000 énumérations.
Jared
20
Comment les gens qui suggèrent des solutions horribles comme celle-ci obtiennent-ils une réputation de 64K? Je déteste penser que tout le code merdique est lancé sur la base des réponses de ces contributeurs
Dexygen