Java: ¿Verificar si enum contiene una cadena dada?

169

Aquí está mi problema: estoy buscando (si es que existe) el enum equivalente de ArrayList.contains(); .

Aquí hay una muestra de mi problema de código:

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

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

Ahora, me doy cuenta de que una ArrayListdeStrings sería la mejor ruta aquí, pero tengo que ejecutar mi contenido de enumeración a través de un interruptor / caso en otro lugar. De ahí mi problema.

Suponiendo que algo como esto no exista, ¿cómo podría hacerlo?

Jared
fuente
El conmutador / caso con cadenas se implementa a partir de Java 7
AndreyP

Respuestas:

206

Esto debería hacerlo:

public static boolean contains(String test) {

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

    return false;
}

De esta manera, no tiene que preocuparse por agregar valores de enumeración adicionales más adelante, todos están marcados.

Editar: si la enumeración es muy grande, puede pegar los valores en un HashSet:

public static HashSet<String> getEnums() {

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

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

  return values;
}

Entonces puedes simplemente hacer: lo values.contains("your string")que devuelve verdadero o falso.

Richard H
fuente
12
esa es una implicación muy pobre: ​​Choice.valueOf (prueba) es lo que quieres (w / try / catch)
bestsss
17
Bestsss, esta es claramente la solución más adecuada. Lanzar una excepción para implementar un tipo de método exist () es una mala práctica. Si bien puede pensar que su implementación es más eficiente debido a que no se ve O (n), está en el marco subyacente que no es visible. También usar try {} catch agrega sobrecarga. Además, simplemente no es bonito.
jluzwick
25
@Jared, definitivamente valorOf. Simplemente tome su excepción y devuelva falso. Para aquellos que dicen lo contrario, si observan la implementación, ya utiliza un Mapa, y los desarrolladores de JDK tienen una oportunidad mucho mejor de optimizar esto. La API arroja una excepción, que es una práctica discutible (en lugar de devolver nulo), pero cuando se trata de una API que arroja una excepción, vaya con ella, no reinvente la rueda.
Yishai
2
@jluzwick try / catch gastos generales es una simple instrucción de salto cuando no se toma, no se optimiza el uso de la excepción en el bloque catch. Temer intentar / atrapar la causa de la pérdida de rendimiento es una mala práctica.
bestsss
22
contiene () es preferible sobre valueOf () con una excepción. ¿Por qué? Debido a que "las excepciones son, como su nombre lo indica, deben usarse solo para condiciones excepcionales; nunca deben usarse para el flujo de control ordinario" (Joshua Bloch, "Java efectivo").
james.garriss
226

Utilice la biblioteca lang3 de Apache commons en su lugar

 EnumUtils.isValidEnum(MyEnum.class, myValue)
RaphC
fuente
33
Tenga en cuenta que para aquellos interesados, la implementación subyacente que usaron es simplemente la solución try / catch (@since 3.0 @version $ Id: EnumUtils.java 1199894 2011-11-09 17: 53: 59Z ggregory $).
Jonathan Gawrych
1
Hace que mi código sea más simple, así que no me importa si usa una excepción para el control de flujo (realmente no deberían) ... Sería bueno si cambiaran eso.
jpangamarca
1
¿La guayaba también contiene una solución como esta?
Cypress Frankenfeld
50

Puedes usar 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 espera que la verificación falle con frecuencia, es mejor que use un bucle simple como lo han demostrado otros, si sus enumeraciones contienen muchos valores, quizás builda HashSeto similar de sus valores de enumeración convertidos en una cadena y consulte eso HashSet.

nos
fuente
8
No creo que Exception sea la mejor opción en tal caso.
GokcenG
66
Try and Catch debería ser el último recurso. Try and Catch son
demasiado
2
confiar en excepciones de tiempo de ejecución para hacer lógica de negocios, además de costoso, no es tan legible. con respecto a las excepciones marcadas, es diferente, porque son parte del negocio.
Luís Soares
2
Esto también evita que alguien active un intervalo amplio en las excepciones lanzadas para encontrar casos excepcionales reales que se están volviendo a intentar. (o al menos hace que sea muy molesto hacerlo). Use excepciones para casos excepcionales.
Nickolay Kondratyev
44
EnumUtils.isValidEnum(MyEnum.class, myValue)usa una lógica similar y la OMI tiene sentido agregar una biblioteca completa para esta tarea trivial
Vikramjit Roy
37

Si está utilizando Java 1.8, puede elegir Stream + Lambda para implementar esto:

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

Aun mejor:

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 devastador
fuente
Gracias por la solución optimista
Parth Patel
19

Guavas Enums podría ser tu amigo

Por ejemplo, esto:

enum MyData {
    ONE,
    TWO
}

@Test
public void test() {

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

Primero puede convertir la enumeración a Lista y luego usar el método de lista contiene

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
fuente
Esta debería ser la respuesta aceptada. La conversión a la lista es la forma más limpia de hacerlo. Se ahorra toda la discusión (lateral) sobre el "uso adecuado de las excepciones" en Java en las otras respuestas aquí.
Manuel
Lance IllegalArgumentException si el valor no existe.
mkyong
10

Pocas suposiciones:
1) Sin intento / captura, ya que es un control de flujo excepcional
2) El método 'contiene' debe ser rápido, ya que generalmente se ejecuta varias veces.
3) El espacio no es limitado (común para soluciones ordinarias)

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
fuente
10

Aquí se han mencionado un par de bibliotecas, pero extraño la que estaba buscando: ¡Primavera!

Existe ObjectUtils # usesConstant, que no distingue entre mayúsculas y minúsculas de forma predeterminada, pero puede ser estricto si lo desea. Se usa así:

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

Nota: Utilicé el método sobrecargado aquí para demostrar cómo usar la comprobación de mayúsculas y minúsculas. Puede omitir el booleano para tener un comportamiento que no distinga entre mayúsculas y minúsculas.

Sin embargo, tenga cuidado con las enumeraciones grandes, ya que no usan la implementación del Mapa como algunos lo hacen ...

Como beneficio adicional, también proporciona una variante entre mayúsculas y minúsculas del valueOf: ObjectUtils # caseInsensitiveValueOf

Pim Hazebroek
fuente
7

Puedes usar esto

YourEnum {A1, A2, B1, B2}

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

La actualización sugerida por @ wightwulf1944 se incorpora para hacer que la solución sea más eficiente.

greperror
fuente
44
Esta implementación es ineficiente. Esto itera a través de los valores de enumeración para crear un nuevo conjunto, luego crea una secuencia, que itera a través del conjunto resultante. Esto significa que se crea un nuevo Set y Stream cada vez que se llama a la función, y el uso stream()en un set significa que está iterando en cada elemento del conjunto en lugar de aprovechar la tabla hash subyacente que será más rápida. Para mejorar esto, es mejor almacenar en caché el conjunto creado y utilizar su contains()método en su lugar. Si debe obtener una transmisión, úsela Arrays.stream()en su lugar.
Subaru Tashiro
3

No creo que exista, pero puedes hacer algo como esto:

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

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

Editar:

Vea la versión de Richard de esto, ya que es más apropiado ya que esto no funcionará a menos que lo convierta para usar Strings, lo que Richards hace.

jluzwick
fuente
pero creo que el OP quiere probar una cadena?
Richard H
Si! Jaja. Este método no sería tan efectivo ya que sabemos que la elección está en las enumeraciones. Tu modificación es más correcta.
jluzwick
1
Si desea trabajar en algún subconjunto de las enumeraciones (y no sus nombres), es mejor mirar EnumSet. download.oracle.com/javase/6/docs/api/java/util/EnumSet.html
Yishai
Tal vez una pregunta estúpida, pero ¿por qué no está .values()documentado en download.oracle.com/javase/6/docs/api/java/lang/Enum.html ?
Anónimo
Esa es una gran pregunta. Tienes razón, no existe en la documentación y tampoco existe en la fuente de Enum. Supongo que está presente en una de las implementaciones de Enum o hay alguna regla JLS que lo permite. Además, todos los objetos de Colección tienen esto, y esto puede verse como una Colección, aunque no necesariamente está implementando una Colección.
jluzwick
3

Java Streams proporciona una forma elegante de hacerlo

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

Devuelve: verdadero si alguno de los elementos de la secuencia coincide con el valor proporcionado; de lo contrario, falso

Sourabh
fuente
2

¿Por qué no combinar la respuesta de Pablo con 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
fuente
Por favor no Vea otra respuesta anterior que es equivalente a la suya: stackoverflow.com/a/4936872/103412
Torsten
1

Este enfoque se puede usar para verificar cualquiera Enum, puede agregarlo a una Utilsclase:

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

Úselo de esta manera:

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

Creé la siguiente clase para esta validación

public class EnumUtils {

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

}

ejemplo de uso:

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

Puede usar valueOf("a1")si desea buscar por String

Amir Afghani
fuente
3
pero eso no es elegante ... si el valor no existe, arroja una excepción. así que probablemente debes rodearlo con try catch
Kasturi
Eso arrojará una excepción si el valor no existe
Richard H
¿Menos elegante que recorrer las opciones de enumeración buscando un objeto coincidente?
jprete
0

Es una enumeración, esos son valores constantes, por lo que si está en una declaración de interruptor, solo está haciendo algo como esto:

case: val1
case: val2

Además, ¿por qué necesitarías saber qué se declara como una constante?

Woot4Moo
fuente
Este bit de código no está en dicha declaración de cambio, eso está en otra parte. Simplemente estaba afirmando que en este caso, la enumeración es necesaria ya que otros sugirieron que use una ArrayList en su lugar.
Jared
@Jared eso tiene mucho más sentido ahora
Woot4Moo
@Jared, sin embargo, eso realmente no importa, ya que ya conoce los valores que existen. Básicamente, el enum equivalente de list.contains () es MyEnum.MyAwesomeValue
Woot4Moo
0

Con guayaba es aún más simple:

boolean isPartOfMyEnum(String myString){

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

}
chaiyachaiya
fuente
Como dijo Kioria , esto no funcionará. MyEnum.values()devuelve una matriz de instancias MyEnum y MyEnum.value().toString()devuelve una representación de cadena de este objeto de matriz (solo una cadena como "[LMyEnum; @ 15b94ed3")
user2137020
Debe llamar en .name()lugar de .toString()(a menos que anule el método toString predeterminado). Consulte esto para obtener más información: Diferencia entre enum .name () y .toString ()
Gaʀʀʏ
0

Esto combina todos los enfoques de los métodos anteriores y debe tener un rendimiento equivalente. Se puede usar para cualquier enumeración, incluye la solución "Editar" de @Richard H y usa Excepciones para valores no válidos como @bestsss. El único inconveniente es que la clase necesita ser especificada, pero eso convierte esto en un trazador de líneas.

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

}

usuario2910265
fuente
0
com.google.common.collect.Sets.newHashSet(MyEnum.values()).contains("myValue")
cnmuc
fuente
0

solución para verificar si el valor está presente y obtener un valor de enumeración a cambio:

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

Esta funciona para mí:

Arrays.asList(YourEnum.values()).toString().contains("valueToCheck");
EDiaz
fuente
3
Su versión devolverá verdadero incluso si YourEnum contiene "valueToCheckBlaBla", porque "valueToCheck" estará presente en la representación de cadena de toda la lista.
Nicko
0

Si está utilizando Java 8 o superior, puede hacer esto:

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

Puede hacerlo como un método contiene:

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

o simplemente puede usarlo con su bloque de código:

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

también puedes usar: com.google.common.base.Enums

Enums.getIfPresent (varEnum.class, varToLookFor) devuelve un Opcional

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

Rabhi salim
fuente
0

Yo solo escribiría

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

Enum # equals solo funciona con la comparación de objetos.

Swadeshi
fuente
-1
public boolean contains(Choices value) {
   return EnumSet.allOf(Choices.class).contains(value);
}
Anton
fuente
Eso no funcionará. establecer objetos de enumeración, mientras que lo está buscando una cadena.
iTake
ahora la respuesta no se ajusta a la pregunta, ya que se trataba de String :)
iTake
-11

enumson bastante poderosos en Java. Puede agregar fácilmente un containsmétodo a su enumeración (como agregaría un método a una clase):

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
fuente
quieres decir s.equals("b1") || s.equals("b2")??
Jigar Joshi
3
Probablemente esta no sea la mejor manera de hacerlo, ya que tendrá que agregar nuevo s.equals("xx")para cada enumeración que agregue más adelante.
jluzwick
1
Y habrá más de 1000 enumeraciones.
Jared
20
¿Cómo las personas que sugieren soluciones horribles como esta obtienen 64K de reputación? Odio pensar en todo el código de mierda que se lanzó allí en base a las respuestas de estos contribuyentes
Dexygen