Отримання всіх імен у переліченні як рядка []


96

Який найпростіший та / або найкоротший спосіб отримати імена елементів перерахування як масив Strings?

Я маю на увазі це те, що якщо, наприклад, я мав такий перелік:

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

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

names()метод буде повертати масив { "NEW", "RUNNABLE", "BLOCKED", "WAITING", "TIMED_WAITING", "TERMINATED" }.


2
Чому не існує власного методу Enum, який робить саме те, що це поза мною ... а також get (index) для спрощення отримання value ()
marcolopes 06.03.14

Відповіді:


91

Ось enumоднокласник для будь-якого класу:

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

Pre Java 8 все ще є одноклассником, хоча і менш елегантним:

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

Що ви назвали б так:

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

Якщо ви просто хочете щось просте для жорстко закодованого класу переліку:

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

2
Не найшвидший спосіб зробити цю роботу, але хороший регулярний вираз.
ceklock

2
У рішенні Java 8, поза межами методу, замість e.getEnumConstants()вас можна використовуватиYourEnumName.values()
Eido95,

1
@ Eido95 using YourEnumName.values()викликає статичний метод для класу, який не можна використовувати повторно для будь-якого перерахування. Використання getEnumConstants()робіт для будь-якого класу переліку. Ідея цього методу полягає у відтворенні корисного методу, який можна викликати як останній рядок відповіді.
Чеська

якщо вам потрібна власна мітка для вашого переліку, перевизначте toString () у своєму класі переліку.
ralphgabb

Ви можете пояснити Class<? extends Enum<?>> e?
Кармагеддон,

63

Створіть String[]масив для імен і викличте статичний values()метод, який повертає всі значення переліку, потім перебирайте значення та заповнюйте масив імен.

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
Навіщо дзвонити values()13 разів, щоразу створюючи новий масив, замість того, щоб кешувати масив у локальній змінній? Навіщо використовувати toString (), який можна замінити замість name()?
JB Nizet

@JBNizet хе-хе, я насправді спробував це в моїй IDE і спосіб ліниво створювати державний масив, я здогадуюсь, хе-хе, в будь-якому разі редагував це зараз :)
PermGenError

2
Ви все ще не повертаєте імена, а значення toString ().
JB Nizet

@JBNizet хм, tbh, я справді не знаю, що до цього часу існував name()метод. як дурно з мене: P дякую, що повідомили :)
PermGenError

1
Я прийняв цю відповідь, але подав редакцію, яка покращує код, щоб його було легше читати.
Костянтин

14

Ось елегантне рішення з використанням Apache Commons Lang 3 :

EnumUtils.getEnumList(State.class)

Хоча він повертає список, ви можете легко перетворити його за допомогою list.toArray()


5
EnumUtils.getEnumList(enumClass)повертає значення переліку, а не рядки. Ви можете використовувати EnumUtils.getEnumMap(enumClass).keySet(), але порядок може бути іншим.
Dan Halbert

11

Якщо ви можете використовувати Java 8, це працює добре (альтернатива пропозиції Юри, більш ефективна):

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

9

З java 8:

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

2
Хоча це, ймовірно, працює, ця реалізація є досить неефективною, спочатку створюючи ArrayList, додаючи всі елементи, потім створюючи масив і копіюючи все знову.
Даніель Бімшас

Незабаром "усі" будуть використовувати потоки для виконання найосновніших операцій ... і це не дуже гарна ідея.
marcolopes

враховуючи той факт, що кількість елементів у переліченнях зазвичай досить мала, я думаю, що рішення Юри буде добре для багатьох випадків використання. Природно, це буде залежати від конкретної ситуації ...
stefan.m

6

Я б написав це так

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

Щось на зразок цього:

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

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

  return names;
}

Документація рекомендує використовувати toString()замість цього name()у більшості випадків , але ви прямо вказали назву тут.


1
Хоча це не має особливого значення, звичайний цикл for пропонує кращу продуктивність, ніж цикл foreach у цій ситуації. Плюс, values()викликається двічі, коли це потрібно викликати лише один раз.
FThompson

1
@Vulcan Я думаю, що продуктивність була б проблемою лише з цим, якби її запускали сотні тисяч разів. Крім того, values()це метод, генерований компілятором, тому я думаю, він досить оптимізований. Нарешті, я пам’ятаю, десь читав, що цикли for-each ефективніші за стандартні, поступові для циклів, але знову ж таки - це просто не має значення .
Костянтин

@Konstantin, вважаючи, що цикл foreach у java реалізований зі стандартом for loop, де коли б ви не читали, що цикли foreach стають все ефективнішими, це неправильно.
sage88

1
@ sage88, я мав на увазі цикли, в яких ви отримуєте елементи, використовуючи числовий індекс, який ви збільшуєте в кожному циклі. Наприклад: for (int i = 0; i < a.length; i++) /* do stuff */;У цьому випадку, кожна-петля , є , по суті, більш ефективним. Швидкий пошуковий запит з цього питання дає нам таке: stackoverflow.com/questions/2113216/…
Костянтин

@Konstantin Кожного разу, коли у вас є структура даних, яка дозволяє довільний доступ, як це відбувається зі значеннями enum, яка повертає масив, цикл C-стилю для швидше, ніж використання для кожного циклу. Кожен цикл повинен генерувати ітератор, що робить його повільнішим. Посилання, яке ви розмістили, дає приклад використання стандарту for у зв’язаному списку за допомогою get (i), що, звичайно, гірше. При використанні структури даних із невипадковим доступом для кожного циклу швидше, але для масивів він має більші накладні витрати. Крім того, оскільки це примітивний масив, а не ArrayList, від .size () не вражається продуктивність.
sage88

3

Інші способи:

Перший

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

Інший шлях

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

1
У цьому випадку цеtoString() спрацьовує, але в загальному випадку ні, оскільки toString()може бути замінено. Використовуйте , .name()щоб надійно отримати примірник ім'я у вигляді рядка.
Богемський

2

Моє рішення, з маніпулюванням рядками (не найшвидшим, але компактним):

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

та Java7 сумісний!
Сила Водолія

1

я б зробив це таким чином (але я б, мабуть, зробив імена немодифікованим набором замість масиву):

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

Я б також зробив щось подібне, якби мені потрібно було неодноразово отримувати імена перерахувань, але оскільки names()метод викликається лише помірковано, я думаю, що генерувати масив на місці - це добре.
Костянтин

1

У мене така сама потреба і використовую загальний метод (всередині класу 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;
}

І просто визначте СТАТИКУ всередині переліку ...

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

Перелічення Java дійсно сумують за іменами () та методами (індекс), вони дійсно корисні.


1

звичайний спосіб (передбачений каламбур):

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

1

Я провів невеликий тест на рішення @ Bohemian. Продуктивність краща, якщо замість цього використовувати наївний цикл.

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

Ви повинні надати статистику щодо результативності, тоді люди можуть самостійно судити, чи є втрата ефективності значною чи ні. Я вважаю, що більшість людей не матимуть значень 100+ або 1000+ для Enum.
Рауні Ліллемет,


0

Якщо ви хочете найкоротше, можете спробувати

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

Не найкоротший :) Подивіться мою відповідь на однокласний вкладиш (який все ще викликає лише vales()один раз)
Богемія

0

Тільки думка: можливо, вам не потрібно створювати метод для повернення значень переліку як масив рядків.

Навіщо потрібен масив рядків? Можливо, вам потрібно перетворити значення лише тоді, коли ви їх використовуєте, якщо вам коли-небудь потрібно буде це зробити.

Приклади:

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

Іншим способом зробити це в Java 7 або раніше можна було б використовувати Guava:

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

0

Спробуйте це:

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

Ви можете додати значення переліку до списку рядків і перетворити в масив:

    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

Найпростіший спосіб:

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

Де категорія - це Enumназва

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.