Obtendo todos os nomes em um enum como uma String []

95

Qual é a maneira mais fácil e / ou curta possível de obter os nomes dos elementos enum como um array de Strings?

O que quero dizer com isso é que se, por exemplo, eu tivesse o seguinte enum:

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

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

o names()método retornaria a matriz { "NEW", "RUNNABLE", "BLOCKED", "WAITING", "TIMED_WAITING", "TERMINATED" }.

Konstantin
fonte
2
Por que não existe um método Enum nativo que faça exatamente isso está além de mim ... e também um get (index) para simplificar o get de value ()
marcolopes

Respostas:

91

Aqui está uma linha para qualquer enumclasse:

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

O pré Java 8 ainda é de uma linha, embora menos elegante:

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

Que você chamaria assim:

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

Se você deseja algo simples para uma classe enum codificada:

public static String[] names() {
    return Arrays.toString(State.values()).replaceAll("^.|.$", "").split(", ");
}
Boêmio
fonte
2
Não é a maneira mais rápida de fazer o trabalho, mas regex agradável.
ceklock
2
Na solução Java 8, fora do escopo do método, em vez de e.getEnumConstants()você pode usarYourEnumName.values()
Eido95
1
@ Eido95 using YourEnumName.values()está chamando um método estático na classe, que não pode ser reutilizado para qualquer enum. Usando getEnumConstants()funciona para qualquer classe enum. A ideia com este método é recriar um método utilitário que você pode chamar como a última linha da resposta.
Boêmio
se você quiser um rótulo personalizado para seu enum, substitua toString () em sua classe de enum.
ralphgabb
Você pode explicar Class<? extends Enum<?>> e?
Carmageddon
63

Crie uma String[]matriz para os nomes e chame o values()método estático que retorna todos os valores enum e, em seguida, itere sobre os valores e preencha a matriz de nomes.

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
fonte
9
Por que chamar values()13 vezes, gerando um novo array a cada vez, em vez de armazenar o array em cache em uma variável local? Por que usar toString (), que pode ser substituído, em vez de name()?
JB Nizet
@JBNizet heheh, na verdade tentei isso no meu IDE e com muita preguiça de criar uma matriz de estado eu acho, hehe, de qualquer forma eu editei agora :)
PermGenError
2
Você ainda não está retornando os nomes, mas os valores toString ().
JB Nizet
@JBNizet hmm, tbh, eu realmente não sabia que havia um name()método até agora. que estúpido da minha parte: P obrigado por me avisar :)
PermGenError
1
Aceitei essa resposta, mas enviei uma edição que melhora o código para ficar mais fácil de ler.
Konstantin
14

Aqui está uma solução elegante usando Apache Commons Lang 3 :

EnumUtils.getEnumList(State.class)

Embora retorne uma lista, você pode converter a lista facilmente com list.toArray()

Sergio Trapiello
fonte
5
EnumUtils.getEnumList(enumClass)retorna os valores enum, não as Strings. Você poderia usar EnumUtils.getEnumMap(enumClass).keySet(), mas a ordem pode ser diferente.
Dan Halbert
11

Se você pode usar Java 8, isso funciona bem (alternativa à sugestão de Yura, mais eficiente):

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

Com java 8:

Arrays.stream(MyEnum.values()).map(Enum::name)
                    .collect(Collectors.toList()).toArray();
Yura Galavay
fonte
2
Embora provavelmente funcione, esta implementação é bastante ineficiente, primeiro criando um ArrayList, adicionando todos os elementos, depois criando um array e copiando tudo novamente.
Daniel Bimschas,
Em breve, "todos" estarão usando streams para realizar as operações mais básicas ... e isso não é uma ideia muito boa.
marcolopes
considerando o fato de que o número de elementos em enums geralmente é muito pequeno, acho que a solução de Yura será adequada para muitos casos de uso. Naturalmente, vai depender da situação específica ...
stefan.m
6

Eu escreveria assim

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
fonte
3

Algo assim faria:

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

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

  return names;
}

A documentação recomenda o uso em toString()vez de name()na maioria dos casos , mas você pediu explicitamente o nome aqui.

Dave Webb
fonte
1
Embora não seja particularmente importante, um loop for regular oferece melhor desempenho do que um loop foreach nessa situação. Além disso, values()é chamado duas vezes quando precisa ser chamado apenas uma vez.
FThompson
1
@Vulcan Acho que o desempenho só seria um problema se fosse executado centenas de milhares de vezes. Além disso, values()é um método gerado pelo compilador, então acho que é bastante otimizado. Finalmente, lembro-me de ter lido em algum lugar que os loops for-each são mais eficientes do que os loops for incrementais padrão, mas, novamente - isso simplesmente não importa .
Konstantin
@Konstantin considerando que o loop foreach em java é implementado com um loop for padrão, é incorreto sempre que você ler que os loops foreach são cada vez mais eficientes.
sage88 de
1
@ sage88 Eu estava me referindo a loops for nos quais você recupera elementos usando um índice numérico, que você incrementa a cada ciclo. Por exemplo: for (int i = 0; i < a.length; i++) /* do stuff */;neste caso, os loops for-each são , de fato, mais eficientes. Uma rápida pesquisa sobre o assunto nos dá o seguinte: stackoverflow.com/questions/2113216/…
Konstantin
@Konstantin Sempre que você tiver uma estrutura de dados que permite acesso aleatório, como é o caso dos valores enum, que retorna uma matriz, um loop for no estilo C é mais rápido do que usar um loop for for. O loop for each deve gerar um iterador, o que o torna mais lento. O link que você postou dá um exemplo de como usar um loop for padrão em uma lista vinculada usando get (i), que é obviamente pior. Ao usar uma estrutura de dados de acesso não aleatório, o for each loop é mais rápido, mas em matrizes tem uma sobrecarga maior. Além disso, como é um array primitivo, não um ArrayList, não há nenhum impacto de desempenho de .size ().
sage88
3

Outras maneiras:

Primeiro

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

Outro jeito

Stream.of(FieldType.values()).map(f -> f.toString()).toArray(String[]::new);
Durgpal Singh
fonte
1
No presente caso, toString()funciona, mas no caso geral isso não acontece, porque toString()pode ser ser substituído. Use .name()para obter o nome da instância de forma confiável como String.
Boêmio
2

Minha solução, com manipulação de strings (não é a mais rápida, mas é 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
fonte
e compatível com java7!
Aquarius Power
1

Eu faria assim (mas provavelmente faria dos nomes um conjunto não modificável em vez de uma 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
fonte
Eu faria algo assim também se precisasse obter os nomes de enum repetidamente, mas como o names()método é chamado apenas com moderação, acho que é bom gerar uma matriz no local.
Konstantin
1

Tenho a mesma necessidade e uso um método genérico (dentro de uma classe 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;
}

E basta definir um ESTÁTICO dentro do enum ...

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

Enums Java realmente perdem os métodos names () e get (index), eles são muito úteis.

marcolopes
fonte
1

a maneira comum (trocadilho intencional):

String[] myStringArray=new String[EMyEnum.values().length];
for(EMyEnum e:EMyEnum.values())myStringArray[e.ordinal()]=e.toString();
Aquarius Power
fonte
1

Fiz um pequeno teste na solução do @Bohemian. O desempenho é melhor ao usar o loop ingênuo.

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);
}
Mandril
fonte
Você deve fornecer estatísticas sobre desempenho, para que as pessoas possam julgar por si mesmas se a perda de desempenho é significativa ou não. Suponho que a maioria das pessoas não terá mais de 100 ou 1000 valores para um Enum.
Rauni Lillemets
1

org.apache.commons.lang3.EnumUtils.getEnumMap (State.class) .keySet ()

wutzebaer
fonte
O que é EnumUtils?
Stephan
@wutzebaer downvoting porque esta alma não está completa - ela não retorna a matriz
Linha
0

Se você quiser o mais curto, você pode tentar

public static String[] names() {
    String test = Arrays.toString(values());
    return text.substring(1, text.length()-1).split(", ");
}
Peter Lawrey
fonte
Não é o mais curto :) Veja minha resposta para uma linha (que ainda invoca apenas vales()uma vez)
Boêmio
0

Apenas um pensamento: talvez você não precise criar um método para retornar os valores do enum como um array de strings.

Por que você precisa do array de strings? Talvez você só precise converter os valores ao usá-los, se precisar fazer isso.

Exemplos:

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

Outra maneira de fazer isso no Java 7 ou anterior seria usar o Guava:

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

Experimente isto:

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

Você pode colocar valores enum em uma lista de strings e convertê-los em array:

    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.
fonte
0

A maneira mais fácil:

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

Onde a categoria é o Enumnome

Ilja Tarasovs
fonte