Obtener todos los nombres en una enumeración como una cadena []

95

¿Cuál es la forma más fácil y / o más corta posible de obtener los nombres de los elementos enum como una matriz de Strings?

Lo que quiero decir con esto es que si, por ejemplo, tuviera la siguiente enumeración:

public enum State {
    NEW,
    RUNNABLE,
    BLOCKED,
    WAITING,
    TIMED_WAITING,
    TERMINATED;

    public static String[] names() {
        // ...
    }
}

el names()método devolvería la matriz { "NEW", "RUNNABLE", "BLOCKED", "WAITING", "TIMED_WAITING", "TERMINATED" }.

Konstantin
fuente
2
¿Por qué no hay un método nativo de enumeración que hace exactamente que es más allá de mí ... y también un get (índice) para simplificar la escapada de valor ()
marcolopes

Respuestas:

91

Aquí hay una sola línea para cualquier enumclase:

public static String[] getNames(Class<? extends Enum<?>> e) {
    return Arrays.stream(e.getEnumConstants()).map(Enum::name).toArray(String[]::new);
}

Pre Java 8 sigue siendo de una sola línea, aunque menos elegante:

public static String[] getNames(Class<? extends Enum<?>> e) {
    return Arrays.toString(e.getEnumConstants()).replaceAll("^.|.$", "").split(", ");
}

Que llamarías así:

String[] names = getNames(State.class); // any other enum class will work

Si solo desea algo simple para una clase de enumeración codificada:

public static String[] names() {
    return Arrays.toString(State.values()).replaceAll("^.|.$", "").split(", ");
}
Bohemio
fuente
2
No son las formas más rápidas de hacer el trabajo, pero una expresión regular agradable.
ceklock
2
En la solución Java 8, fuera del alcance del método, en lugar de e.getEnumConstants()usted puede usarYourEnumName.values()
Eido95
1
@ Eido95 YourEnumName.values()está llamando a un método estático en la clase, que no se puede reutilizar para ninguna enumeración. El uso getEnumConstants()funciona para cualquier clase de enumeración. La idea con este método es recrear un método de utilidad que pueda llamar como la última línea de la respuesta.
Bohemio
si desea una etiqueta personalizada para su enumeración, anule toString () en su clase de enumeración.
ralphgabb
¿Puedes explicarme Class<? extends Enum<?>> e?
Carmageddon
63

Cree una String[]matriz para los nombres y llame al values()método estático que devuelve todos los valores de enumeración, luego itere sobre los valores y complete la matriz de nombres.

public static String[] names() {
    State[] states = values();
    String[] names = new String[states.length];

    for (int i = 0; i < states.length; i++) {
        names[i] = states[i].name();
    }

    return names;
}
PermGenError
fuente
9
¿Por qué llamar values()13 veces, generando una nueva matriz cada vez, en lugar de almacenar en caché la matriz en una variable local? ¿Por qué usar toString (), que es reemplazable, en lugar de name()?
JB Nizet
@JBNizet jejeje, de hecho probé esto en mi IDE y me sentí perezoso para crear una matriz de estado, supongo, jeje, de todos modos lo edité ahora :)
PermGenError
2
Todavía no devuelve los nombres, sino los valores de toString ().
JB Nizet
@JBNizet hmm, tbh, realmente no sé que había un name()método hasta ahora. qué estúpido de mi parte: P gracias por
hacérmelo
1
Acepté esta respuesta, pero envié una edición que mejora el código para que sea más fácil de leer.
Konstantin
14

Aquí tienes una solución elegante usando Apache Commons Lang 3 :

EnumUtils.getEnumList(State.class)

Aunque devuelve una lista, puede convertir la lista fácilmente con list.toArray()

Sergio Trapiello
fuente
5
EnumUtils.getEnumList(enumClass)devuelve los valores de enumeración, no las cadenas. Podría utilizar EnumUtils.getEnumMap(enumClass).keySet(), pero el orden puede ser diferente.
Dan Halbert
11

Si puede usar Java 8, esto funciona bien (alternativa a la sugerencia de Yura, más eficiente):

public static String[] names() {
    return Stream.of(State.values()).map(State::name).toArray(String[]::new);
}
Kris Boyd
fuente
9

Con java 8:

Arrays.stream(MyEnum.values()).map(Enum::name)
                    .collect(Collectors.toList()).toArray();
Yura Galavay
fuente
2
Si bien probablemente funciona, esta implementación es bastante ineficiente, primero crea una ArrayList, agrega todos los elementos, luego crea una matriz y copia todo nuevamente.
Daniel Bimschas
Pronto, "todo el mundo" utilizará las transmisiones para realizar las operaciones más básicas ... y esa no es una muy buena idea.
marcolopes
considerando el hecho de que la cantidad de elementos en las enumeraciones suele ser bastante pequeña, creo que la solución de Yura estará bien para muchos casos de uso. Naturalmente, dependerá de la situación específica ...
stefan.m
6

Lo escribiria asi

public static String[] names() {

    java.util.LinkedList<String> list = new LinkedList<String>();
    for (State s : State.values()) {
        list.add(s.name());
    }

    return list.toArray(new String[list.size()]);
}
Raymond Chenon
fuente
3

Algo como esto haría:

public static String[] names() {
  String[] names = new String[values().length];
  int index = 0;

  for (State state : values()) {
    names[index++] = state.name();
  }

  return names;
}

La documentación recomienda usar en toString()lugar de name()en la mayoría de los casos , pero ha pedido explícitamente el nombre aquí.

Dave Webb
fuente
1
Aunque no importa en particular, un bucle for regular ofrece un mejor rendimiento que un bucle foreach en esta situación. Además, values()se llama dos veces cuando solo es necesario llamar una vez.
FThompson
1
@Vulcan Creo que el rendimiento solo sería un problema con esto si se ejecutara cientos de miles de veces. Además, values()es un método generado por el compilador, así que supongo que está bastante optimizado. Finalmente, recuerdo haber leído en alguna parte que los bucles for-each son más eficientes que los bucles for estándar, incrementales, pero, de nuevo, simplemente no importa .
Konstantin
@Konstantin considerando que el bucle foreach en java se implementa con un bucle for estándar, dondequiera que lea que los bucles foreach son cada vez más eficientes es incorrecto.
sage88
1
@ sage88 Me refería a los bucles for en los que recupera elementos utilizando un índice numérico, que incrementa en cada ciclo. Por ejemplo: for (int i = 0; i < a.length; i++) /* do stuff */;en este caso, los bucles for-each son , de hecho, más eficientes. Una búsqueda rápida de SO sobre el tema nos da esto: stackoverflow.com/questions/2113216/…
Konstantin
@Konstantin Siempre que tenga una estructura de datos que permita el acceso aleatorio, como es el caso de los valores de enumeración, que devuelve una matriz, un bucle for estilo C es más rápido que usar un bucle for cada bucle. El para cada bucle tiene que generar un iterador, lo que lo hace más lento. El enlace que publicó ofrece un ejemplo del uso de un bucle for estándar en una lista enlazada usando get (i), que por supuesto es peor. Cuando se utiliza una estructura de datos de acceso no aleatorio, el para cada bucle es más rápido, pero en las matrices tiene una sobrecarga mayor. Además, dado que es una matriz primitiva, no una ArrayList, no hay impacto en el rendimiento de .size ().
sage88
3

Otras formas:

El primero

Arrays.asList(FieldType.values())
            .stream()
            .map(f -> f.toString())
            .toArray(String[]::new);

Otra manera

Stream.of(FieldType.values()).map(f -> f.toString()).toArray(String[]::new);
Durgpal Singh
fuente
1
En este caso toString()funciona, pero en el caso general no lo hace, ya toString()que se puede anular. Úselo .name()para obtener de manera confiable el nombre de la instancia como una cadena.
Bohemio
2

Mi solución, con manipulación de cadenas (no es la más rápida, pero es compacta):

public enum State {
    NEW,
    RUNNABLE,
    BLOCKED,
    WAITING,
    TIMED_WAITING,
    TERMINATED;

    public static String[] names() {
        String valuesStr = Arrays.toString(State.values());
        return valuesStr.substring(1, valuesStr.length()-1).replace(" ", "").split(",");
    }
}
ceklock
fuente
y compatible con java7.
Aquarius Power
1

Lo haría de esta manera (pero probablemente convertiría los nombres en un conjunto no modificable en lugar de una matriz):

import java.util.Arrays;
enum State {
    NEW,RUNNABLE,BLOCKED,WAITING,TIMED_WAITING,TERMINATED;
    public static final String[] names=new String[values().length];
    static {
        State[] values=values();
        for(int i=0;i<values.length;i++)
            names[i]=values[i].name();
    }
}
public class So13783295 {
    public static void main(String[] args) {
        System.out.println(Arrays.asList(State.names));
    }
}
Ray Tayek
fuente
También haría algo como esto si tuviera que obtener repetidamente los nombres de enumeración, pero dado que el names()método solo se llama con moderación, creo que generar una matriz en el lugar está bien.
Konstantin
1

Tengo la misma necesidad y uso un método genérico (dentro de una clase ArrayUtils):

public static <T> String[] toStringArray(T[] array) {
    String[] result=new String[array.length];
    for(int i=0; i<array.length; i++){
        result[i]=array[i].toString();
    }
    return result;
}

Y simplemente defina una ESTÁTICA dentro de la enumeración ...

public static final String[] NAMES = ArrayUtils.toStringArray(values());

Las enumeraciones de Java realmente extrañan los métodos de names () y get (index), son realmente útiles.

marcolopes
fuente
1

la forma ordinaria (juego de palabras intencionado):

String[] myStringArray=new String[EMyEnum.values().length];
for(EMyEnum e:EMyEnum.values())myStringArray[e.ordinal()]=e.toString();
Poder de Acuario
fuente
1

Hice una pequeña prueba en la solución de @ Bohemian. El rendimiento es mejor cuando se usa un bucle ingenuo.

public static <T extends Enum<?>> String[] getEnumNames(Class<T> inEnumClass){
    T [] values = inEnumClass.getEnumConstants();
    int len = values.length;
    String[] names = new String[len];
    for(int i=0;i<values.length;i++){
        names[i] = values[i].name();
    }
    return names;
}

//Bohemian's solution
public static String[] getNames(Class<? extends Enum<?>> e) {
    return Arrays.stream(e.getEnumConstants()).map(Enum::name).toArray(String[]::new);
}
Arrojar
fuente
Debe proporcionar las estadísticas sobre el rendimiento, luego las personas pueden juzgar por sí mismas si la pérdida de rendimiento es significativa o no. Supongo que la mayoría de las personas no tendrán más de 100 o más de 1000 valores para una enumeración.
Rauni Lillemets
0

Si quieres lo más corto puedes probar

public static String[] names() {
    String test = Arrays.toString(values());
    return text.substring(1, text.length()-1).split(", ");
}
Peter Lawrey
fuente
No es el más corto :) Vea mi respuesta para una sola línea (que todavía solo se invoca vales()una vez)
Bohemio
0

Solo un pensamiento: tal vez no necesite crear un método para devolver los valores de la enumeración como una matriz de cadenas.

¿Por qué necesita la matriz de cadenas? Tal vez solo necesite convertir los valores cuando los use, si alguna vez necesita hacerlo.

Ejemplos:

for (State value:values()) {
    System.out.println(value); // Just print it.
}

for (State value:values()) {
    String x = value.toString(); // Just iterate and do something with x.
}

// If you just need to print the values:
System.out.println(Arrays.toString(State.values()));
ceklock
fuente
0

Otra forma de hacerlo en Java 7 o anterior sería usar Guava:

public static String[] names() {
    return FluentIterable.from(values()).transform(Enum::name).toArray(String.class);
}
Justin Lee
fuente
0

Prueba esto:

public static String[] vratAtributy() {
    String[] atributy = new String[values().length];
    for(int index = 0; index < atributy.length; index++) {
        atributy[index] = values()[index].toString();
    }
    return atributy;
}
Matiseli
fuente
0

Puede poner valores de enumeración en la lista de cadenas y convertirlos en una matriz:

    List<String> stateList = new ArrayList<>();

            for (State state: State.values()) {
                stateList.add(state.toString());
            }

    String[] stateArray = new String[stateList.size()];
    stateArray = stateList.toArray(stateArray);
Veljko P.
fuente
0

La forma más fácil:

Category[] category = Category.values();
for (int i = 0; i < cat.length; i++) {
     System.out.println(i  + " - " + category[i]);
}

Donde Categoría es Enumnombre

Ilja Tarasovs
fuente