Получение всех имен в перечислении как String []


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

Ответы:


92

Вот однострочник для любого 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 YourEnumName.values()вызывает статический метод класса, который нельзя повторно использовать ни для одного перечисления. Использование getEnumConstants()работает для любого класса enum. Идея этого метода состоит в том, чтобы воссоздать служебный метод, который вы можете вызывать как последнюю строку ответа.
Богемский

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

Не могли бы вы объяснить Class<? extends Enum<?>> e?
Carmageddon

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 хм, тбх, я действительно не знал, что name()до сих пор был метод. как глупо с моей стороны: P Спасибо, что
сообщили

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

15

Вот элегантное решение с использованием Apache Commons Lang 3 :

EnumUtils.getEnumList(State.class)

Хотя он возвращает список, вы можете легко преобразовать список с помощью list.toArray()


6
EnumUtils.getEnumList(enumClass)возвращает значения перечисления, а не строки. Вы можете использовать EnumUtils.getEnumMap(enumClass).keySet(), но порядок может быть другим.
Дэн Халберт

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

4

Другие способы:

Первый

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

Другой путь

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

2
В этом случае toString()работает, но в общем случае нет, потому что toString()это можно изменить. Используйте , .name()чтобы надежно получить экземпляр имя в виде строки.
Bohemian

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 более эффективны, чем стандартные, инкрементные циклы for, но опять же - это просто не имеет значения .
Константин

@Konstantin, учитывая, что цикл foreach в java реализован со стандартным циклом for, где бы вы ни читали, что циклы foreach становятся все более эффективными, неверно.
sage88

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

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

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 действительно отсутствуют методы names () и get (index), они действительно полезны.


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.
Rauni Lillemets


0

Если вам нужен самый короткий, вы можете попробовать

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

Не самый короткий :) См. Мой ответ для однострочника (который все еще вызывает только vales()один раз)
Bohemian

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.