Recupero di tutti i nomi in un'enumerazione come stringa []


96

Qual è il modo più semplice e / o più breve possibile per ottenere i nomi degli elementi enum come un array di Strings?

Quello che intendo con questo è che se, ad esempio, avessi la seguente enumerazione:

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

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

il names()metodo restituirà l'array { "NEW", "RUNNABLE", "BLOCKED", "WAITING", "TIMED_WAITING", "TERMINATED" }.


2
Perché non esiste un metodo Enum nativo che faccia esattamente questo è al di là di me ... e anche un get (index) per semplificare il get of value ()
marcolopes

Risposte:


92

Ecco una frase per qualsiasi enumclasse:

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

Pre Java 8 è ancora un one-liner, anche se meno elegante:

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

Che chiameresti così:

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

Se vuoi solo qualcosa di semplice per una classe enum hard-coded:

public static String[] names() {
    return Arrays.toString(State.values()).replaceAll("^.|.$", "").split(", ");
}

2
Non il modo più veloce per fare il lavoro, ma bella regex.
Ceklock

2
Nella soluzione Java 8, al di fuori dell'ambito del metodo, invece di e.getEnumConstants()utilizzareYourEnumName.values()
Eido95

1
@ Eido95 using YourEnumName.values()sta chiamando un metodo statico sulla classe, che non può essere riutilizzato per nessuna enum. Utilizzo di getEnumConstants()opere per qualsiasi classe enum. L'idea con questo metodo è ricreare un metodo di utilità che puoi chiamare come l'ultima riga della risposta.
Bohemian

se vuoi un'etichetta personalizzata per la tua enum, sovrascrivi toString () nella tua classe enum.
ralphgabb

Puoi spiegare per favore Class<? extends Enum<?>> e?
Carmageddon,

63

Creare un String[]array per i nomi e chiamare il values()metodo statico che restituisce tutti i valori enum, quindi iterare sui valori e popolare l'array dei nomi.

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
Perché chiamare values()13 volte, generando ogni volta un nuovo array, invece di memorizzare nella cache l'array in una variabile locale? Perché usare toString (), che è sovrascrivibile, invece di name()?
JB Nizet

@JBNizet heheh, in realtà l'ho provato nel mio IDE e in modo pigro per creare un array di stato immagino, hehe, comunque l'ho modificato ora :)
PermGenError

2
Non stai ancora restituendo i nomi, ma i valori toString ().
JB Nizet

@ JBNizet hmm, tbh, non sapevo davvero che ci fosse un name()metodo fino ad ora. che stupido da parte mia: P grazie per
avermelo

1
Ho accettato questa risposta, ma ho inviato una modifica che migliora il codice in modo che sia più facile da leggere.
Konstantin

14

Ecco un'elegante soluzione che utilizza Apache Commons Lang 3 :

EnumUtils.getEnumList(State.class)

Sebbene restituisca un elenco, puoi convertire facilmente l'elenco con list.toArray()


5
EnumUtils.getEnumList(enumClass)restituisce i valori enum, non le stringhe. Potresti usare EnumUtils.getEnumMap(enumClass).keySet(), ma l'ordine potrebbe essere diverso.
Dan Halbert

11

Se puoi usare Java 8, funziona bene (alternativa al suggerimento di Yura, più efficiente):

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

9

Con java 8:

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

2
Anche se probabilmente funziona, questa implementazione è piuttosto inefficiente, creando prima un ArrayList, aggiungendo tutti gli elementi, quindi creando un array e copiando di nuovo tutto.
Daniel Bimschas

Presto "tutti" useranno i flussi per eseguire le operazioni di base ... e questa non è una buona idea.
marcolopes

considerando il fatto che il numero di elementi nelle enumerazioni è solitamente piuttosto piccolo, penso che la soluzione di Yura andrà bene per molti casi d'uso. Naturalmente, dipenderà dalla situazione specifica ...
stefan.m

6

Lo scriverei così

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

Qualcosa del genere farebbe:

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

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

  return names;
}

La documentazione consiglia di utilizzare al toString()posto di name()nella maggior parte dei casi , ma qui hai chiesto esplicitamente il nome.


1
Sebbene non sia particolarmente importante, un ciclo for regolare offre prestazioni migliori rispetto a un ciclo foreach in questa situazione. Inoltre, values()viene chiamato due volte quando deve essere chiamato solo una volta.
FThompson

1
@Vulcan Penso che le prestazioni sarebbero un problema con questo solo se fosse eseguito centinaia di migliaia di volte. Inoltre, values()è un metodo generato dal compilatore, quindi immagino sia piuttosto ottimizzato. Infine, ricordo di aver letto da qualche parte che i cicli for-each sono più efficienti dei cicli standard, incrementali per i cicli, ma poi di nuovo - non importa .
Konstantin

@ Konstantin considerando che il ciclo foreach in java è implementato con uno standard per ciclo, dove mai si legge che i cicli foreach sono sempre più efficienti non è corretto.
sage88

1
@ sage88 Mi riferivo ai cicli for in cui si recuperano elementi utilizzando un indice numerico, che si incrementa ad ogni ciclo. Ad esempio: for (int i = 0; i < a.length; i++) /* do stuff */;in questo caso, i cicli for-each sono , in effetti, più efficienti. Una rapida ricerca SO sull'argomento ci dà questo: stackoverflow.com/questions/2113216/…
Konstantin

@Konstantin Ogni volta che si dispone di una struttura dati che consente l'accesso casuale, come nel caso dei valori enum, che restituisce un array, un ciclo for in stile C è più veloce rispetto all'utilizzo di un ciclo for. Il ciclo for each deve generare un iteratore, il che lo rende più lento. Il collegamento che hai pubblicato fornisce un esempio di utilizzo di un ciclo for standard su un elenco collegato utilizzando get (i), che ovviamente è peggio. Quando si utilizza una struttura dati ad accesso non casuale, il ciclo for each è più veloce, ma sugli array ha un overhead maggiore. Inoltre, poiché si tratta di un array primitivo, non di un ArrayList, non ci sono perdite di prestazioni da .size ().
sage88

3

Altri modi:

Il primo

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

Altro modo

Stream.of(FieldType.values()).map(f -> f.toString()).toArray(String[]::new);

1
In questo caso toString()funziona, ma nel caso generale non funziona, perché toString()può essere sovrascritto. Utilizzare .name()per ottenere in modo affidabile il nome dell'istanza come String.
Bohemian

2

La mia soluzione, con la manipolazione delle stringhe (non la più veloce, ma è compatta):

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 compatibile con java7!
Aquarius Power

1

lo farei in questo modo (ma probabilmente renderei i nomi un insieme non modificabile invece di un array):

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

Farei qualcosa di simile anche se avessi bisogno di ottenere ripetutamente i nomi enum, ma poiché il names()metodo viene chiamato solo con parsimonia, penso che la generazione di un array sul posto vada bene.
Konstantin

1

Ho la stessa esigenza e utilizzo un metodo generico (all'interno di una 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 definire uno STATICO all'interno dell'enumerazione ...

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

Le enumerazioni Java mancano davvero di metodi names () e get (index), sono davvero utili.


1

il modo ordinario (gioco di parole):

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

1

Ho fatto un po 'di prova sulla soluzione di @ Bohemian. Le prestazioni sono migliori quando si utilizza invece il loop 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);
}

Dovresti fornire le statistiche sulle prestazioni, quindi le persone possono giudicare da sole se la perdita di prestazioni è significativa o meno. Suppongo che la maggior parte delle persone non avrà 100+ o 1000+ valori per un Enum.
Rauni Lillemets


0

Se vuoi il più corto puoi provare

public static String[] names() {
    String test = Arrays.toString(values());
    return text.substring(1, text.length()-1).split(", ");
}

Non il più breve :) Vedi la mia risposta per una battuta (che ancora invoca solo vales()una volta)
Bohemian

0

Solo un pensiero: forse non è necessario creare un metodo per restituire i valori di enum come un array di stringhe.

Perché hai bisogno dell'array di stringhe? Forse hai solo bisogno di convertire i valori quando li usi, se mai ne avrai bisogno.

Esempi:

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

Un altro modo per farlo in Java 7 o versioni precedenti sarebbe usare Guava:

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

0

Prova questo:

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

Puoi inserire valori enum nell'elenco di stringhe e convertirli in 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

La via più facile:

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

Dove la categoria è il Enumnome

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.