Pobieranie wszystkich nazw w wyliczeniu jako String []

96

Jaki jest najłatwiejszy i / lub najkrótszy możliwy sposób uzyskania nazw elementów wyliczeniowych w postaci tablicy Strings?

Mam przez to na myśli to, że gdybym na przykład miał następujące wyliczenie:

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

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

names()sposób powróci tablicy { "NEW", "RUNNABLE", "BLOCKED", "WAITING", "TIMED_WAITING", "TERMINATED" }.

Konstantin
źródło
2
Dlaczego nie ma natywnej metody Enum, która robi dokładnie to, co jest poza mną ... a także get (index), aby uprościć pobieranie wartości ()
marcolopes

Odpowiedzi:

91

Oto jedna linijka dla każdej enumklasy:

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

Pre Java 8 jest nadal jednolinijkowym, choć mniej eleganckim:

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

To nazwałbyś tak:

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

Jeśli potrzebujesz czegoś prostego dla zakodowanej na stałe klasy wyliczeniowej:

public static String[] names() {
    return Arrays.toString(State.values()).replaceAll("^.|.$", "").split(", ");
}
Czeski
źródło
2
Nie jest to najszybszy sposób na wykonanie tej pracy, ale fajny regex.
ceklock
2
W rozwiązaniu Java 8, poza zakresem metody, zamiast e.getEnumConstants()ciebie możesz użyćYourEnumName.values()
Eido95
1
@ Eido95 użycie YourEnumName.values()jest wywołaniem statycznej metody na klasie, której nie można ponownie wykorzystać dla żadnego wyliczenia. Używanie getEnumConstants()działa dla dowolnej klasy wyliczeniowej. Ideą tej metody jest odtworzenie metody narzędziowej, którą można wywołać jak ostatnią linię odpowiedzi.
Bohemian
jeśli chcesz mieć niestandardową etykietę dla swojego wyliczenia, zastąp toString () w swojej klasie wyliczeniowej.
ralphgabb
Czy możesz wyjaśnić Class<? extends Enum<?>> e?
Carmageddon
63

Utwórz String[]tablicę nazw i wywołaj values()metodę statyczną , która zwraca wszystkie wartości wyliczenia, a następnie wykonaj iterację po wartościach i zapełnij tablicę nazw.

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
źródło
9
Po co wywoływać values()13 razy, generując za każdym razem nową tablicę, zamiast buforować tablicę w zmiennej lokalnej? Po co używać metody toString (), która jest zastępowalna, zamiast name()?
JB Nizet,
@JBNizet heheh, faktycznie próbowałem tego w moim IDE i leniwy, aby utworzyć tablicę stanu, myślę, że hehe, tak czy inaczej iedited to teraz :)
PermGenError
2
Nadal nie zwracasz nazw, ale wartości toString ().
JB Nizet,
@JBNizet hmm, tbh, naprawdę nie wiem, że name()do tej pory istniała metoda. jak głupi z mojej strony: P dzięki, że dałeś mi znać :)
PermGenError
1
Zaakceptowałem tę odpowiedź, ale przesłałem edycję, która ulepsza kod, aby był łatwiejszy do odczytania.
Konstantin
14

Oto eleganckie rozwiązanie wykorzystujące Apache Commons Lang 3 :

EnumUtils.getEnumList(State.class)

Mimo że zwraca listę, możesz ją łatwo przekonwertować za pomocą list.toArray()

Sergio Trapiello
źródło
5
EnumUtils.getEnumList(enumClass)zwraca wartości wyliczenia, a nie Strings. Możesz użyć EnumUtils.getEnumMap(enumClass).keySet(), ale kolejność może być inna.
Dan Halbert
11

Jeśli możesz używać Java 8, działa to ładnie (alternatywa dla sugestii Yury, bardziej wydajna):

public static String[] names() {
    return Stream.of(State.values()).map(State::name).toArray(String[]::new);
}
Kris Boyd
źródło
9

W Javie 8:

Arrays.stream(MyEnum.values()).map(Enum::name)
                    .collect(Collectors.toList()).toArray();
Yura Galavay
źródło
2
Chociaż prawdopodobnie działa, ta implementacja jest dość nieefektywna, najpierw tworzy ArrayList, dodaje wszystkie elementy, a następnie tworzy tablicę i ponownie kopiuje wszystko.
Daniel Bimschas
W najbliższym czasie „wszyscy” będą używać strumieni do wykonywania najbardziej podstawowych operacji ... a to nie jest dobry pomysł.
marcolopes
biorąc pod uwagę fakt, że liczba elementów w wyliczeniach jest zwykle raczej niewielka, myślę, że rozwiązanie Yury będzie dobre w wielu przypadkach użycia. Oczywiście będzie to zależało od konkretnej sytuacji ...
stefan.m
6

Napisałbym to w ten sposób

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
źródło
3

Coś takiego zrobiłoby:

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

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

  return names;
}

Dokumentacja zaleca używanie toString()zamiast name()w większości przypadków , ale tutaj wyraźnie poprosiłeś o podanie nazwy.

Dave Webb
źródło
1
Chociaż nie ma to większego znaczenia, w tej sytuacji zwykła pętla for zapewnia lepszą wydajność niż pętla foreach. Ponadto values()jest wywoływany dwukrotnie, gdy trzeba go wywołać tylko raz.
FThompson
1
@Vulcan Myślę, że wydajność byłaby problemem tylko wtedy, gdyby była uruchamiana setki tysięcy razy. Poza tym values()jest to metoda wygenerowana przez kompilator, więc myślę, że jest dość zoptymalizowana. Wreszcie, pamiętam, że czytałem gdzieś, że pętle for-each są bardziej wydajne niż standardowe, inkrementalne pętle for, ale z drugiej strony - to po prostu nie ma znaczenia .
Konstantin
@Konstantin biorąc pod uwagę, że pętla foreach w Javie jest zaimplementowana za pomocą standardowej pętli for, gdziekolwiek przeczytasz, że pętle foreach są coraz bardziej wydajne, jest niepoprawne.
sage88
1
@ sage88 odnosiłem się do pętli, w których pobierasz elementy za pomocą indeksu numerycznego, który jest zwiększany w każdym cyklu. Na przykład: w for (int i = 0; i < a.length; i++) /* do stuff */;tym przypadku pętle for-each w rzeczywistości bardziej wydajne. Szybkie wyszukiwanie SO w tej sprawie daje nam to: stackoverflow.com/questions/2113216/…
Konstantin
@Konstantin Za każdym razem, gdy masz strukturę danych, która umożliwia losowy dostęp, jak ma to miejsce w przypadku wartości wyliczeniowych, które zwracają tablicę, pętla for w stylu C jest szybsza niż użycie pętli for each. Pętla for each musi wygenerować iterator, dzięki czemu jest wolniejsza. Opublikowane łącze podaje przykład użycia standardowej pętli for na połączonej liście przy użyciu funkcji get (i), co jest oczywiście gorsze. W przypadku korzystania ze struktury danych o dostępie innym niż losowy pętla for each jest szybsza, ale w przypadku tablic ma większy narzut. Ponadto, ponieważ jest to prymitywna tablica, a nie ArrayList, funkcja .size () nie wpływa na wydajność.
sage88
3

Inne sposoby:

Pierwszy

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

Inny sposób

Stream.of(FieldType.values()).map(f -> f.toString()).toArray(String[]::new);
Durgpal Singh
źródło
1
W tym przypadku toString()działa, ale w ogólnym przypadku nie, ponieważ toString()można to nadpisać. Służy .name()do niezawodnego pobierania nazwy instancji w postaci ciągu.
Bohemian
2

Moje rozwiązanie z manipulacją strunami (nie najszybsze, ale zwarte):

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
źródło
i zgodne z java7!
Aquarius Power
1

zrobiłbym to w ten sposób (ale prawdopodobnie utworzyłbym nazwy jako niemodyfikowalny zestaw zamiast tablicy):

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
źródło
Zrobiłbym też coś takiego, gdybym musiał wielokrotnie pobierać nazwy wyliczeń, ale ponieważ names()metoda jest wywoływana oszczędnie, myślę, że generowanie tablicy na miejscu jest w porządku.
Konstantin
1

Mam taką samą potrzebę i używam metody ogólnej (wewnątrz klasy 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;
}

I po prostu zdefiniuj STATIC wewnątrz wyliczenia ...

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

Wyliczeniom Java naprawdę brakuje metod names () i get (index), są one naprawdę pomocne.

markolopy
źródło
1

zwykły sposób (gra słów zamierzona):

String[] myStringArray=new String[EMyEnum.values().length];
for(EMyEnum e:EMyEnum.values())myStringArray[e.ordinal()]=e.toString();
Moc Wodnika
źródło
1

Zrobiłem mały test na rozwiązaniu @ Bohemian. Wydajność jest lepsza, gdy zamiast tego używa się naiwnej pętli.

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);
}
Głaskanie pod brodę
źródło
Powinieneś podać statystyki dotyczące wydajności, a następnie ludzie mogą sami ocenić, czy spadek wydajności jest znaczący, czy nie. Przypuszczam, że większość ludzi nie będzie miała wartości 100+ lub 1000+ dla Enum.
Rauni Lillemets
0

Jeśli chcesz najkrótszego, możesz spróbować

public static String[] names() {
    String test = Arrays.toString(values());
    return text.substring(1, text.length()-1).split(", ");
}
Peter Lawrey
źródło
Nie najkrótsza :) Zobacz moją odpowiedź na jedną linijkę (która wciąż przywołuje się tylko vales()raz)
Bohemian
0

Tylko myśl: może nie musisz tworzyć metody zwracającej wartości wyliczenia jako tablicę ciągów.

Dlaczego potrzebujesz tablicy ciągów? Może będziesz musiał tylko przekonwertować wartości, gdy ich używasz, jeśli kiedykolwiek będziesz musiał to zrobić.

Przykłady:

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
źródło
0

Innym sposobem na zrobienie tego w Javie 7 lub wcześniejszej byłoby użycie Guava:

public static String[] names() {
    return FluentIterable.from(values()).transform(Enum::name).toArray(String.class);
}
Justin Lee
źródło
0

Spróbuj tego:

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
źródło
0

Możesz umieścić wartości wyliczenia na liście ciągów i przekonwertować na tablicę:

    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.
źródło
0

Najłatwiejszy sposób:

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

Gdzie Kategoria to Enumnazwa

Ilja Tarasovs
źródło