Перетворення від enum порядкового до enum типу


316

У мене є тип enum, ReportTypeEnumякий передається між методами у всіх моїх класах, але мені потім потрібно передати це за URL-адресою, щоб я використовував порядковий метод, щоб отримати значення int. Після того, як я отримаю його на іншій мовій сторінці JSP, мені потрібно перетворити її назад до такої, ReportTypeEnumщоб я міг продовжувати її передавати.

Як я можу перетворити порядковий на ReportTypeEnum?

Використання Java 6 SE.


1
Досі немає Java 6 EE (AFAIK). Є Java SE 6 і Java EE 5.
Hosam Aly

Відповіді:


632

Щоб перетворити порядковий знак в репрезентацію перерахунку, ви можете зробити це:

ReportTypeEnum value = ReportTypeEnum.values()[ordinal];

Зверніть увагу на межі масиву.

Зауважте, що кожен виклик values()повертає знову клонований масив, який може негативно впливати на продуктивність. Ви можете захопити кешування масиву, якщо він буде викликатися часто.

Приклад коду щодо кешуванняvalues() .


Цю відповідь було відредаговано, щоб включити відгуки, подані всередині коментарів


Я реалізував це рішення, і воно не працює для мене. Це повертає порядкове значення, яке не гарантується відповідності порядку, в якому додаються перелічені типи. Я не знаю, що саме на це виступає ця відповідь, але я все ж хотів попередити людей
IcedDante

@IcesDante: порядковий параметр, безумовно, гарантовано відповідає порядку перерахування значень у джерелі. Якщо ви спостерігаєте іншу поведінку, то щось інше повинно бути не так. Однак моя відповідь вище є неоптимальною з усіх причин, викладених в інших відповідях.
Йоахім Зауер

@JoachimSauer Можливо, IcedDante означає, що порядковий номер може не збігатися, якщо він був створений і збережений у попередній версії джерела, в якій значення перерахунків були в іншому порядку.
LarsH

137

Це майже напевно погана ідея . Звичайно, якщо порядковий номер де-факто зберігається (наприклад, тому, що хтось зробив закладки URL-адреси) - це означає, що ви завжди повинні зберігати enumвпорядкування в майбутньому, що може не бути очевидним для обслуговуючого коду внизу лінії.

Чому замість цього не кодувати enumвикористання myEnumValue.name()(і декодування через ReportTypeEnum.valueOf(s))?


24
Що робити, якщо змінити назву enum (але зберегти впорядкування)?
Арне Евертссон

6
@Arne - Я думаю, що це набагато рідше, ніж якась недосвідчена людина, яка збирається і додає valueані на початку, ані в правильному алфавітному / логічному положенні. (Під логічним я маю на увазі, наприклад, TimeUnitзначення мають логічне положення)
oxbow_lakes

7
Я, звичайно, вважаю за краще примушувати замовлення переліків, а не ім'я мого переліку ... саме тому я вважаю за краще зберігати порядковий, а не ім'я переліків у базі даних. Крім того, краще використовувати маніпуляції з int, а не String ...

8
Я згоден. У загальнодоступному API зміна назви Enum призведе до порушення сумісності із зворотним процесом, але зміна порядку не буде. З цієї причини має сенс використовувати ім'я як "ключ"
Ноель

3
Зберігання порядкових значень полегшує переклад ваших ідей на інші мови. Що робити, якщо вам потрібно написати якийсь компонент на C?
QED

94

Якщо я буду використовувати values()багато:

enum Suit {
   Hearts, Diamonds, Spades, Clubs;
   public static final Suit values[] = values();
}

Тим часом де б не було.java:

Suit suit = Suit.values[ordinal];

Майте на увазі межі масиву.


3
+1 це далеко не найкраще рішення ІМХО, оскільки можна передавати порядки, особливо в android.os.Message.
likejudo

9
Це дуже хвилює, оскільки масиви є змінними . Незважаючи на те, що значення [] є остаточними, це не заважає Suit.values[0] = Suit.Diamonds;десь у вашому коді. В ідеалі цього ніколи не відбудеться, але загальний принцип не виставляти змінних полів все ще дотримується. Для цього підходу розгляньте Collections.unmodifiableListзамість цього використання тощо.
Мшник

Як щодо - приватні статичні кінцеві значення костюма [] = значення (); public static Suit [] getValues ​​() {повернути значення; }
Пратюш

4
@Pratyush, який би зробив змінну масиву незмінною, але не її вміст. Я все ще можу зробити getValues ​​() [0] = щосьElse;
Калабацин

Погодьтеся, таким чином, суть не піддається Suit values[]прямому чи опосередкованому (як це було зазначено getValues()), я працюю з публічним методом, коли ordinalзначення повинно бути надіслане як аргумент і повернути Suitподання Suit values[]. Справа тут (плитка запитання від початку) - створити тип enum із перерахунку
Мануель Йордан

13

Я погоджуюсь з більшістю людей, що використання ординалів - це, мабуть, погана ідея. Зазвичай я вирішую цю проблему, надаючи enum приватному конструктору, який може приймати, наприклад, значення БД, а потім створювати статичну fromDbValueфункцію, аналогічну тій, що відповідає у відповіді Яна.

public enum ReportTypeEnum {
    R1(1),
    R2(2),
    R3(3),
    R4(4),
    R5(5),
    R6(6),
    R7(7),
    R8(8);

    private static Logger log = LoggerFactory.getLogger(ReportEnumType.class);  
    private static Map<Integer, ReportTypeEnum> lookup;
    private Integer dbValue;

    private ReportTypeEnum(Integer dbValue) {
        this.dbValue = dbValue;
    }


    static {
        try {
            ReportTypeEnum[] vals = ReportTypeEnum.values();
            lookup = new HashMap<Integer, ReportTypeEnum>(vals.length);

            for (ReportTypeEnum  rpt: vals)
                lookup.put(rpt.getDbValue(), rpt);
         }
         catch (Exception e) {
             // Careful, if any exception is thrown out of a static block, the class
             // won't be initialized
             log.error("Unexpected exception initializing " + ReportTypeEnum.class, e);
         }
    }

    public static ReportTypeEnum fromDbValue(Integer dbValue) {
        return lookup.get(dbValue);
    }

    public Integer getDbValue() {
        return this.dbValue;
    }

}

Тепер ви можете змінити порядок, не змінюючи пошук і навпаки.


ЦЕ правильна відповідь. Я здивований, що набрав так мало балів порівняно з іншими більш прямими, але потенційно недійсними відповідями (через зміни коду в майбутньому).
Калабацин

8

Ви можете використовувати статичну таблицю пошуку:

public enum Suit {
  spades, hearts, diamonds, clubs;

  private static final Map<Integer, Suit> lookup = new HashMap<Integer, Suit>();

  static{
    int ordinal = 0;
    for (Suit suit : EnumSet.allOf(Suit.class)) {
      lookup.put(ordinal, suit);
      ordinal+= 1;
    }
  }

  public Suit fromOrdinal(int ordinal) {
    return lookup.get(ordinal);
  }
}

3
Дивіться також Enums .
trashgod

11
Оце Так! Просто вау! Це, звичайно, акуратно, але ... ви знаєте - програміст C всередині мене кричить від болю, бачачи, що ви виділяєте повноцінний HashMap і виконуєте пошук всередині нього ПОСЛІДНО, щоб по суті управляти 4 константами: лопатами, сердечками, алмази та клуби! Програміст змінного струму виділить 1 байт для кожного: 'const char CLUBS = 0;' Так ... Так, пошук HashMap - це O (1), але об'єм пам'яті та процесора накладних витрат HashMap в цьому випадку робить його на багато порядків повільнішим і ресурсом голодніше, ніж виклик .values ​​() безпосередньо! Недарма Ява є такою свиною пам’яті, якщо люди пишуть так ...
Лешек

2
Не кожна програма вимагає виконання потрійної гри A. У багатьох випадках торгова пам'ять та процесор для безпеки типу, читабельності, ремонтопридатності, підтримки між платформами, збору сміття тощо ... є виправданим. Мови вищого рівня існують не просто так.
січня

3
Але якщо діапазон ключів завжди є 0...(n-1), то масив менше коду і читабельніший; підвищення продуктивності - лише бонус. private static final Suit[] VALUES = values();і public Suit fromOrdinal(int ordinal) { return VALUES[ordinal]; }. Додаткова перевага: негайно виходить з ладу недійсних порядків, а не мовчки повертає null. (Не завжди перевага. Але часто.)
Томас

4

Це те, що я використовую. Я не прикидаюся, що це набагато менш "ефективно", ніж простіші рішення вище. Це робиться - це набагато чіткіше повідомлення про виняток, ніж "ArrayIndexOutOfBounds", коли у вказаному вище рішенні використовується недійсне порядкове значення.

Він використовує той факт, що javadoc EnumSet вказує ітератор, що повертає елементи в їх природному порядку. Є твердження, якщо це невірно.

Тест JUnit4 демонструє, як він використовується.

 /**
 * convert ordinal to Enum
 * @param clzz may not be null
 * @param ordinal
 * @return e with e.ordinal( ) == ordinal
 * @throws IllegalArgumentException if ordinal out of range
 */
public static <E extends Enum<E> > E lookupEnum(Class<E> clzz, int ordinal) {
    EnumSet<E> set = EnumSet.allOf(clzz);
    if (ordinal < set.size()) {
        Iterator<E> iter = set.iterator();
        for (int i = 0; i < ordinal; i++) {
            iter.next();
        }
        E rval = iter.next();
        assert(rval.ordinal() == ordinal);
        return rval;
    }
    throw new IllegalArgumentException("Invalid value " + ordinal + " for " + clzz.getName( ) + ", must be < " + set.size());
}

@Test
public void lookupTest( ) {
    java.util.concurrent.TimeUnit tu = lookupEnum(TimeUnit.class, 3);
    System.out.println(tu);
}

1

Це те, що я роблю на Android з Proguard:

public enum SomeStatus {
    UNINITIALIZED, STATUS_1, RESERVED_1, STATUS_2, RESERVED_2, STATUS_3;//do not change order

    private static SomeStatus[] values = null;
    public static SomeStatus fromInteger(int i) {
        if(SomeStatus.values == null) {
            SomeStatus.values = SomeStatus.values();
        }
        if (i < 0) return SomeStatus.values[0];
        if (i >= SomeStatus.values.length) return SomeStatus.values[0];
        return SomeStatus.values[i];
    }
}

це коротко, і мені не потрібно турбуватися про те, щоб мати виняток у Proguard


1

Ви можете визначити простий метод, наприклад:

public enum Alphabet{
    A,B,C,D;

    public static Alphabet get(int index){
        return Alphabet.values()[index];
    }
}

І використовувати його так:

System.out.println(Alphabet.get(2));

0
public enum Suit implements java.io.Serializable, Comparable<Suit>{
  spades, hearts, diamonds, clubs;
  private static final Suit [] lookup  = Suit.values();
  public Suit fromOrdinal(int ordinal) {
    if(ordinal< 1 || ordinal> 3) return null;
    return lookup[value-1];
  }
}

тестовий клас

public class MainTest {
    public static void main(String[] args) {
        Suit d3 = Suit.diamonds;
        Suit d3Test = Suit.fromOrdinal(2);
        if(d3.equals(d3Test)){
            System.out.println("Susses");
        }else System.out.println("Fails");
    }
}

Я вдячний, що ви ділитесь з нами, якщо у вас є більш ефективний код, Мій перелік величезний і постійно дзвонять тисячі разів.


Я думаю, ви мали на увазі "якщо (порядковий <1 || порядковий> 4) повернути null;"
geowar

0

Отже, один із способів - це те, ExampleEnum valueOfOrdinal = ExampleEnum.values()[ordinal];що працює, і його легко, однак, як було сказано раніше, ExampleEnum.values()повертається новий клонований масив для кожного дзвінка. Це може бути надмірно дорогим. Ми можемо вирішити це, кешуючи масив так ExampleEnum[] values = values(). Також "небезпечно" дозволити модифікацію нашого кешованого масиву. Хтось міг написати, ExampleEnum.values[0] = ExampleEnum.type2;тому я зробив би це приватним методом accessor, який не робить додаткового копіювання.

private enum ExampleEnum{
    type0, type1, type2, type3;
    private static final ExampleEnum[] values = values();
    public static ExampleEnum value(int ord) {
        return values[ord];
    }
}

Ви б використали ExampleEnum.value(ordinal)для отримання значення enum, пов’язаного ізordinal


-1

Кожен enum має ім'я (), яке дає рядок із ім'ям члена enum.

Дано enum Suit{Heart, Spade, Club, Diamond}, Suit.Heart.name()дасть Heart.

Кожен enum має valueOf()метод, який приймає тип enum і рядок, щоб виконати операцію зворотного:

Enum.valueOf(Suit.class, "Heart")повертає Suit.Heart.

Чому хтось би використовував ординари - це поза мною. Це може бути наносекунди швидше, але це не безпечно, якщо члени enum змінюються, оскільки інший розробник може не знати, що код покладається на порядкові значення (особливо на сторінці JSP, про яку йдеться у запитанні, мережа та накладні дані бази повністю переважають над час, не використовуючи ціле число над рядком).


2
Тому що порівняння цілих чисел набагато швидше, ніж порівняння рядків?
HighCommander4

2
Але порядкові зміни зміняться, якщо хтось змінить перерахунок (Додавання / перестановка пам’яті). Іноді мова йде про безпеку, а не про швидкість, особливо на сторінці JSP, де затримка в мережі в 1000 000 разів перевищує різницю між порівнянням масиву цілих чисел (рядка) та одиничним цілим числом.
Тоні БенБрахім

toString може бути замінено, тому він може не повернути ім'я перерахунку. Назва методу () - це те, що дає ім'я enum (воно остаточне)
gerardw

коментарі до коду та версії додатків - теж річ (можливо, формат файлу простіший і менший таким чином)
Джо Пеллетєр

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