Закиньте Int, щоб перерахувати на Java


Відповіді:


586

Спробуйте, MyEnum.values()[x]де xмає бути 0або 1, тобто дійсний порядковий знак для цього перерахунку.

Зауважте, що в Java enums насправді є класами (і значення enum, таким чином, є об'єктами), і, таким чином, ви не можете закинути intабо навіть Integerперерахувати.


116
+1: Ви можете захопити кеш-пам'ять MyEnum.values()як дорогу. тобто якщо ви називаєте це сотні разів.
Пітер Лорі

6
@PeterLawrey Просто для повноти ви можете пояснити, чому це повинно бути повільним? Я не бачу очевидних причин для цього.
Тарраш

48
@Tarrasch, оскільки масиви є змінними, значення () повинні повернути копію масиву елементів на випадок, якщо ви трапили її змінити. Створювати цю копію кожен раз порівняно дорого.
Пітер Лорі

9
@PeterLawrey останнім часом я використовую багато haskell (де все незмінне)! Дякуємо за ваше чітке і стисле пояснення. :)
Тарраш

як отримати 'x'?
Beeing Jk

161

MyEnum.values()[x]- дорога операція. Якщо виступ викликає занепокоєння, ви можете зробити щось подібне:

public enum MyEnum {
    EnumValue1,
    EnumValue2;

    public static MyEnum fromInteger(int x) {
        switch(x) {
        case 0:
            return EnumValue1;
        case 1:
            return EnumValue2;
        }
        return null;
    }
}

44
Якщо ви хочете уникнути обслуговування комутатора, то на клас використання: private final MyEnum [] myEnumValues ​​= MyEnum.values ​​(); Тоді використання: myEnum = myEnumValues ​​[i];
Гілі Нахум

23
@GiliNachum сказав це дивним чином, але проблема з цим рішенням - це ремонтопридатність. Це суперечить принципу DRY, що означає щоразу, коли змінюються значення перерахунків (упорядковуються, додається значення (значення)), видаляються (і) значення), оператор перемикання повинен одночасно оновлюватися. Коментар Гілі змушує Java зберігати узгодженість зі списком значень enum, зміни значень enum взагалі не впливають на цей підхід.
Дандре Еллісон

3
@LorenzoPolidori, чи можете ви пояснити, чому ви вважаєте MyEnum.values()[x]дорогу операцію. Я не знаю, як це працює в деталях, але мені здається, що доступ до елемента в масиві не буде великим завданням, а також постійний час. Якщо масив має бути побудований, це потребує часу O (n), який є тим же часом запуску, що і ваше рішення.
brunsgaard

1
@brunsgaard Я припускаю, що values()кожен раз генерує новий масив, оскільки масиви є мутаційними, тому не можна було б безпечно повертати той самий один раз. Оператори переключення не обов'язково O (n), вони можуть бути складені для переходу таблиць. Тож претензії Лоренцо здаються виправданими.
MikeFHay

1
Версія @Johnson Gili не потребує додаткових змін коду поза змінами в декларації Enum. Якщо ви додасте новий елемент до enum, myEnum = myEnumValues[i]він все одно поверне цей iелемент у Enum без змін.
Дандре Еллісон

45

Якщо ви хочете дати цілі значення, ви можете використовувати структуру, як показано нижче

public enum A
{
        B(0),
        C(10),
        None(11);
        int id;
        private A(int i){id = i;}

        public int GetID(){return id;}
        public boolean IsEmpty(){return this.equals(A.None);}
        public boolean Compare(int i){return id == i;}
        public static A GetValue(int _id)
        {
            A[] As = A.values();
            for(int i = 0; i < As.length; i++)
            {
                if(As[i].Compare(_id))
                    return As[i];
            }
            return A.None;
        }
}

5
+1, оскільки це підкреслює факт, що значення не повинні бути послідовними.
Ровін

Крім того, це здається єдиною відповіддю, яка працює, якщо ви хочете розріджений (або повторний) набір цілих значень, а не використовувати порядки за замовчуванням з 0 .. (count-1). Це може бути важливо, якщо ви взаємодієте з існуючим кодом, наприклад, через мережу.
benkc

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

1
І, залежно від довжини перерахунку, ви можете створити HashMap або використовувати двійковий пошук або щось для цього пошуку, а не робити лінійний пошук щоразу.
benkc

2
Це має бути правильним способом та найкращою практикою для перетворення int в перерахунок, і я думаю, ви можете спростити проблему публічним статичним GetValue (int _id) {for (A a: A.values ​​() {if (a.getId ( ) == _ id) {return a;}} return null;} Позбавтесь від None, isEmpty () та порівняйте () речі.
Chris.Zou

35

Можна спробувати так.
Створення класу з ідентифікатором елемента.

      public Enum MyEnum {
        THIS(5),
        THAT(16),
        THE_OTHER(35);

        private int id; // Could be other data type besides int
        private MyEnum(int id) {
            this.id = id;
        }

        public static MyEnum fromId(int id) {
                for (MyEnum type : values()) {
                    if (type.getId() == id) {
                        return type;
                    }
                }
                return null;
            }
      }

Тепер Виберіть цей Enum, використовуючи id як int.

MyEnum myEnum = MyEnum.fromId(5);

19

Я кешую значення та створюю простий метод статичного доступу:

public static enum EnumAttributeType {
    ENUM_1,
    ENUM_2;
    private static EnumAttributeType[] values = null;
    public static EnumAttributeType fromInt(int i) {
        if(EnumAttributeType.values == null) {
            EnumAttributeType.values = EnumAttributeType.values();
        }
        return EnumAttributeType.values[i];
    }
}

4
Це рішення, яке я зараз використовую. Але IMHO менш заплутано, якщо ви не дасте полям valuesте саме ім'я, як метод values(). Я використовую cachedValuesдля назви поля.
ToolmakerSteve

2
Ефективна та елегантна; скопійовано та вставлено у мій проект :) Єдине, що я змінив - це fromInt (int i), з якого я щойно дзвонив (int i), тому що це трохи зайве, щоб мати підпис два рази.
pipedreambomb

1
чому б не ініціалізувати з самого початку? навіщо чекати першого влучення?
кайзер

9

Переліки Java не мають такого ж типу перерахування «перерахунок до int», що вони роблять у C ++.

Однак, всі перерахунки мають valuesметод, який повертає масив можливих значень enum, так що

MyEnum enumValue = MyEnum.values()[x];

повинен працювати. Це трохи неприємно, і, можливо, буде краще не намагатися перетворити з ints на Enums (або навпаки), якщо можливо.


9

Це не те, що зазвичай робиться, тому я б передумав. Але, сказавши це, основними операціями є: int -> enum за допомогою EnumType.values ​​() [intNum], а enum -> int за допомогою enumInst.ordinal ().

Однак, оскільки будь-яка реалізація значень () не має іншого вибору, крім того, щоб надати вам копію масиву (масиви java ніколи не є лише для читання), вам краще послужити, використовуючи EnumMap для кешування enum -> int відображення.


2
Re "Це не те, що зазвичай робиться": Загальний випадок, коли це корисно: enum відповідає значенням int, що зберігаються в базі даних.
ToolmakerSteve

@ToolmakerSteve ви абсолютно праві, що відображення потрібні. Але чи хочете ви залишити такий вид кодування якомусь АБО Mapper чи інструментарію / бібліотеці?
Dilum Ranatunga


6

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

public Enum MyEnum {
    THIS(5),
    THAT(16),
    THE_OTHER(35);

    private int id; // Could be other data type besides int
    private MyEnum(int id) {
        this.id = id;
    }

    public int getId() {
        return this.id;
    }

    public static Map<Integer, MyEnum> buildMap() {
        Map<Integer, MyEnum> map = new HashMap<Integer, MyEnum>();
        MyEnum[] values = MyEnum.values();
        for (MyEnum value : values) {
            map.put(value.getId(), value);
        }

        return map;
    }
}

Мені потрібно лише конвертувати ідентифікатори в перерахунки в певний час (під час завантаження даних з файлу), тому немає ніякої причини для мене постійно зберігати карту в пам'яті. Якщо вам потрібна карта, щоб вона завжди була доступною, ви завжди можете кешувати її як статичний член вашого класу Enum.


IMHO, якщо мене хвилювало використання пам'яті, я динамічно створював би HashMap - як @ossys, але з іншим кодом, коли кеш є нульовим, а потім додавати другий метод, clearCachedValuesколи ви закінчите його використання (що повертає приватне поле до нуля). Я вважаю MyEnum.fromInt(i)простішим для розуміння, ніж обхід об’єкта карти.
ToolmakerSteve

6

У випадку, якщо він допомагає іншим, варіант, який я віддаю перевагу, який не вказаний тут, використовує функцію Карт Guava :

public enum MyEnum {
    OPTION_1(-66),
    OPTION_2(32);

    private int value;
    private MyEnum(final int value) {
        this.value = value;
    }

    public int getValue() {
        return this.value;
    }

    private static ImmutableMap<Integer, MyEnum> reverseLookup = 
            Maps.uniqueIndex(Arrays.asList(MyEnum.values())), MyEnum::getValue);

    public static MyEnum fromInt(final int id) {
        return reverseLookup.getOrDefault(id, OPTION_1);
    }
}

За умовчанням, яким ви можете скористатися null, ви можете throw IllegalArgumentExceptionабо fromIntможете повернути Optional, будь-яку поведінку ви хочете.


3
Ви повинні згадати, що ви використовуєте Guava. Або ви можете використовувати потоки:Map<Integer, MyEnum> reverseLookup = Arrays.stream(MyEnum.values()).collect(Collectors.toMap(MyEnum::getValue, Function.identity()));
shmosel

1
Можна також визначити getValue()метод.
shmosel

@shmosel На жаль, я пропустив функцію getValue, дякую. Введення загальних версій у stackoverflow не завжди вичерпується. Додано коментар щодо використання Guava із посиланням. Віддайте перевагу потоку методу Гуави.
Чад Бефус

4

Ви можете повторити перерахунок values()перерахувань та порівняти ціле значення enum з наведеним idнижче:

public enum  TestEnum {
    None(0),
    Value1(1),
    Value2(2),
    Value3(3),
    Value4(4),
    Value5(5);

    private final int value;
    private TestEnum(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }

    public static TestEnum  getEnum(int value){
        for (TestEnum e:TestEnum.values()) {
            if(e.getValue() == value)
                return e;
        }
        return TestEnum.None;//For values out of enum scope
    }
}

І використовувати саме так:
TestEnum x = TestEnum.getEnum(4);//Will return TestEnum.Value4
сподіваюся, це допомагає;)


3

На основі відповіді @ChadBefus та коментаря @shmosel я рекомендую скористатися цим. (Ефективний пошук і працює на чистому Java> = 8)

import java.util.stream.Collectors;
import java.util.function.Function;
import java.util.Map;
import java.util.Arrays;

public enum MyEnum {
    OPTION_1(-66),
    OPTION_2(32);

    private int value;
    private MyEnum(final int value) {
        this.value = value;
    }

    public int getValue() {
        return this.value;
    }

    private static Map<Integer, MyEnum> reverseLookup =
        Arrays.stream(MyEnum.values()).collect(Collectors.toMap(MyEnum::getValue, Function.identity()));

    public static MyEnum fromInt(final int id) {
        return reverseLookup.getOrDefault(id, OPTION_1);
    }
    public static void main(String[] args) {
        System.out.println(fromInt(-66).toString());
    }
}

0

Хороший варіант - уникнути конверсії з intнаenum : наприклад, якщо вам потрібно максимальне значення, ви можете порівняти x.ordinal () з y.ordinal () і повернути x або y відповідно. (Вам може знадобитися переупорядкувати значення, щоб зробити таке порівняння значущим.)

Якщо це неможливо, я б зберігав MyEnum.values()у статичний масив.


1
Якщо ви отримуєте int від БД і хочете перетворити його в Enum, що, на мою думку, є дуже поширеним завданням, вам знадобиться int -> перерахунок enum, IMO ..
Yuki Inoue

0

Це та сама відповідь, що і лікарі, але вона показує, як усунути проблему із змінними масивами. Якщо ви використовуєте такий тип підходу через передбачення гілок спочатку, якщо буде мати дуже мало нульового ефекту, і весь код викликує значення змінного масиву () лише один раз. Оскільки обидві змінні є статичними, вони також не будуть споживати n * пам’яті для кожного використання цього перерахунку.

private static boolean arrayCreated = false;
private static RFMsgType[] ArrayOfValues;

public static RFMsgType GetMsgTypeFromValue(int MessageID) {
    if (arrayCreated == false) {
        ArrayOfValues = RFMsgType.values();
    }

    for (int i = 0; i < ArrayOfValues.length; i++) {
        if (ArrayOfValues[i].MessageIDValue == MessageID) {
            return ArrayOfValues[i];
        }
    }
    return RFMsgType.UNKNOWN;
}

0
enum MyEnum {
    A(0),
    B(1);
    private final int value;
    private MyEnum(int val) {this.value = value;}
    private static final MyEnum[] values = MyEnum.values();//cache for optimization
    public static final getMyEnum(int value) { 
        try {
            return values[value];//OOB might get triggered
        } catch (ArrayOutOfBoundsException e) {
        } finally {
            return myDefaultEnumValue;
        }
    }
}


@shmosel Я вважаю, що винятки значно кращі, якщо поза межами порівняно мало.
Зосо

На чому базується ваша віра?
shmosel

0

У Котліні:

enum class Status(val id: Int) {
    NEW(0), VISIT(1), IN_WORK(2), FINISHED(3), CANCELLED(4), DUMMY(5);

    companion object {
        private val statuses = Status.values().associateBy(Status::id)

        fun getStatus(id: Int): Status? = statuses[id]
    }
}

Використання:

val status = Status.getStatus(1)!!

0

Написав цю реалізацію. Це дозволяє пропускати відсутні значення, негативні значення та зберігає код узгодженим. Карта також кешована. Використовує інтерфейс і потребує Java 8.

Енум

public enum Command implements OrdinalEnum{
    PRINT_FOO(-7),
    PRINT_BAR(6),
    PRINT_BAZ(4);

    private int val;
    private Command(int val){
        this.val = val;
    }

    public int getVal(){
        return val;
    }

    private static Map<Integer, Command> map = OrdinalEnum.getValues(Command.class);
    public static Command from(int i){
        return map.get(i);
    }
}

Інтерфейс

public interface OrdinalEnum{
    public int getVal();

    @SuppressWarnings("unchecked")
    static <E extends Enum<E>> Map<Integer, E> getValues(Class<E> clzz){
        Map<Integer, E> m = new HashMap<>();
        for(Enum<E> e : EnumSet.allOf(clzz))
            m.put(((OrdinalEnum)e).getVal(), (E)e);

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