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" }.


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(", ");
}

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

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()


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

9

Com java 8:

Arrays.stream(MyEnum.values()).map(Enum::name)
                    .collect(Collectors.toList()).toArray();

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

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.


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

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(",");
    }
}

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

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.


1

a maneira comum (trocadilho intencional):

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

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

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


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(", ");
}

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

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

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

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

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

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.