Приведите Int для перечисления в Java

Как правильно преобразовать Int в перечисление в Java, учитывая следующее перечисление?

public enum MyEnum
{
    EnumValue1,
    EnumValue2
}


MyEnum enumValue = (MyEnum) x; //Doesn't work???

15 ответов

Решение

Пытаться MyEnum.values()[x] где x должно быть 0 или же 1т.е. действительный порядковый номер для этого перечисления.

Обратите внимание, что в Java перечисления на самом деле являются классами (и значения перечисления, таким образом, являются объектами), и, таким образом, вы не можете привести int или даже Integer перечислению.

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

Если вы хотите дать целочисленные значения, вы можете использовать структуру, как показано ниже

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

Вы можете попробовать вот так.
Создать класс с идентификатором элемента.

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

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

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

Перечисления Java не имеют того же типа сопоставления перечисления с целым, что и в C++.

Тем не менее, все перечисления имеют values метод, который возвращает массив возможных значений перечисления, так

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

должно сработать. Это немного противно, и, возможно, лучше не пытаться конвертировать из intс Enums (или наоборот), если это возможно.

Это не то, что обычно делается, поэтому я бы пересмотрел. Но, сказав это, основными операциями являются: int -> enum с использованием EnumType.values ​​()[intNum] и enum -> int с использованием enumInst.ordinal().

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

Использование MyEnum enumValue = MyEnum.values()[x];

Вот решение, которое я планирую использовать. Это не только работает с непоследовательными целыми числами, но и должно работать с любым другим типом данных, который вы можете использовать в качестве основного идентификатора для значений перечисления.

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.

Основываясь на ответе @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());
    }
}

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

Вы можете перебирать values() enum и сравните целочисленное значение 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
Надеюсь, это поможет;)

Это должно быть связано с несовпадением индексов с проблемой порядкового значения.

package service.manager;

public enum Command {
    PRINT_FOO(0),
    PRINT_BAR(2),
    PRINT_BAZ(3);

    public int intVal;
    Command(int intVal){
        this.intVal = intVal;
    }

    /**
     * Functionality to ascertain an enum from an int
     * The static block initializes the array indexes to correspond with it's according ordinal value
     * Simply use Command.values[index] or get the int value by e.g. Command.PRINT_FOO.intVal;
     * */
    public static Command values[];
    static{
        int maxVal = -1;
        for(Command cmd : Command.values())
            if(maxVal < cmd.intVal)
                maxVal = cmd.intVal;

        values = new Command[maxVal + 1];

        for(Command cmd : Command.values())
            values[cmd.intVal] = cmd;
    }
}

В Котлине:

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

Это тот же ответ, что и у врачей, но он показывает, как устранить проблему с изменяемыми массивами. Если вы используете такой подход сначала из-за предсказания ветвлений, если эффект будет иметь очень малое значение до нуля, а весь код вызывает функцию изменяемого массива values ​​() только один раз. Поскольку обе переменные являются статическими, они также не будут использовать 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;
}

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

Если это невозможно, я бы сохранил MyEnum.values() в статический массив.

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;
        }
    }
}
Другие вопросы по тегам