Как правильно преобразовать Int в перечисление в Java, учитывая следующее перечисление?
public enum MyEnum
{
EnumValue1,
EnumValue2
}
MyEnum enumValue = (MyEnum) x; //Doesn't work???
Как правильно преобразовать Int в перечисление в Java, учитывая следующее перечисление?
public enum MyEnum
{
EnumValue1,
EnumValue2
}
MyEnum enumValue = (MyEnum) x; //Doesn't work???
Ответы:
Попробуйте, MyEnum.values()[x]
где x
должно быть 0
или 1
, то есть действительный порядковый номер для этого перечисления.
Обратите внимание, что в Java перечисления на самом деле являются классами (и значения перечисления, таким образом, являются объектами), и, таким образом, вы не можете привести int
или даже Integer
перечислить.
MyEnum.values()
как его дорого. т.е. если вы звоните это сотни раз.
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;
}
}
MyEnum.values()[x]
дорогой операцией. Я не знаю, как это работает в деталях, но мне кажется, что доступ к элементу в массиве не будет большой проблемой, то есть постоянным временем. Если массив должен быть собран, это занимает O (n) время, то есть то же время выполнения, что и ваше решение.
values()
каждый раз генерируется новый массив, потому что массивы изменчивы, поэтому было бы небезопасно возвращать один и тот же один раз несколько раз. Операторы switch не обязательно O (n), они могут быть скомпилированы для перехода по таблицам. Таким образом, претензии Лоренцо кажутся оправданными.
myEnum = myEnumValues[i]
все равно вернете этот i
элемент в Enum без изменений.
Если вы хотите дать целочисленные значения, вы можете использовать структуру, как показано ниже
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];
}
}
values
то же имя, что и метод values()
. Я использую cachedValues
для имени поля.
Перечисления Java не имеют того же типа сопоставления перечисления с целым, что и в C ++.
Тем не менее, все перечисления имеют values
метод, который возвращает массив возможных значений перечисления, поэтому
MyEnum enumValue = MyEnum.values()[x];
должно сработать. Это немного неприятно, и, возможно, лучше не пытаться конвертировать из int
s в Enum
s (или наоборот), если это возможно.
Это не то, что обычно делается, поэтому я бы пересмотрел. Но, сказав это, основными операциями являются: int -> enum с использованием EnumType.values () [intNum] и enum -> int с использованием enumInst.ordinal ().
Однако, поскольку любая реализация values () не имеет другого выбора, кроме как предоставить вам копию массива (java-массивы никогда не доступны только для чтения), вам лучше использовать EnumMap для кэширования отображения enum -> int.
Вот решение, которое я планирую использовать. Это не только работает с непоследовательными целыми числами, но и должно работать с любым другим типом данных, который вы можете использовать в качестве основного идентификатора для значений перечисления.
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.
clearCachedValues
когда вы закончили с его использованием (который устанавливает приватное поле обратно в ноль). Я считаю, что MyEnum.fromInt(i)
легче понять, чем обойти объект карты.
В случае, если это помогает другим, предпочтительный вариант, которого здесь нет в списке, использует функциональность карт 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
любое поведение, которое вы предпочитаете.
Map<Integer, MyEnum> reverseLookup = Arrays.stream(MyEnum.values()).collect(Collectors.toMap(MyEnum::getValue, Function.identity()));
getValue()
метод также.
Вы можете перебирать 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
надеюсь, это поможет;)
Основываясь на ответе @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());
}
}
Хороший вариант - избегать конвертации int
вenum
: например, если вам нужно максимальное значение, вы можете сравнить x.ordinal () с y.ordinal () и вернуть x или y соответственно. (Возможно, вам придется изменить порядок значений, чтобы сделать такое сравнение значимым.)
Если это невозможно, я бы MyEnum.values()
сохранил статический массив.
Это тот же ответ, что и у врачей, но он показывает, как устранить проблему с изменяемыми массивами. Если вы используете такой подход сначала из-за предсказания ветвлений, если эффект будет иметь очень малое значение до нуля, а весь код вызывает функцию изменяемого массива 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;
}
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;
}
}
}
В Котлине:
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)!!
Написал эту реализацию. Он учитывает пропущенные значения, отрицательные значения и поддерживает согласованность кода. Карта также кэшируется. Использует интерфейс и нуждается в Java 8.
Enum
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;
}
}