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


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

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

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


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

9

W Javie 8:

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

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

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.


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

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

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

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.


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

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

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

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

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

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

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

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

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.