将Int转换为Java中的枚举


Answers:


583

尝试MyEnum.values()[x]x必须为0or的地方1,即该枚举的有效序数。

请注意,在Java中,枚举实际上是类(因此,枚举值是对象),因此无法将an int甚至Integer转换为枚举。


115
+1:您可能想缓存MyEnum.values()它的昂贵资源。即,如果您调用它数百次。
彼得·劳瑞

6
@PeterLawrey仅出于完整性考虑,您能解释为什么它应该变慢吗?我认为没有明显的原因。
塔拉施2012年

48
@Tarrasch因为数组是可变的,所以values()必须返回元素数组的副本,以防万一您更改它。每次创建此副本都是相对昂贵的。
彼得·劳瑞

9
@PeterLawrey我最近已经习惯了很多haskell(一切都是不可变的)!感谢您简洁明了的解释。:)
Tarrasch

如何获得“ x”?
Beeing Jk

160

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
如果要避免进行开关维护,请在using类上:private final MyEnum [] myEnumValues = MyEnum.values(); 然后的用法:myEnum = myEnumValues [i];
Gili Nachum 2012年

23
@GiliNachum说的很奇怪,但是这个解决方案的问题是可维护性。它违反了DRY原理,这意味着每当枚举值发生更改(重新排序,添加值,删除值)时,switch语句都必须同时更新。Gili的评论迫使Java保持与枚举值列表的一致性,对枚举值的更改根本不会影响该方法。
Dandre Allison

3
@LorenzoPolidori,您能解释一下为什么您认为这MyEnum.values()[x]是一项昂贵的手术吗?我不知道它是如何工作的,但是对我来说,访问数组中的元素似乎没什么大不了的,也就是恒定的时间。如果必须构建阵列,则需要O(n)时间,这与您的解决方案的运行时间相同。
brunsgaard 2013年

1
我假设@brunsgaard values()每次都会生成一个新数组,因为数组是可变的,因此多次返回相同数组并不安全。switch语句不一定是O(n),它们可以编译为跳转表。因此,洛伦佐的主张似乎是合理的。
MikeFHay 2014年

1
@Johnson Gili的版本不需要在Enum声明中进行任何更改即可。如果您将新项目添加到枚举,myEnum = myEnumValues[i]则仍将返回枚举中的i第e个项目,而无需进行任何更改。
丹迪·艾里森

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是因为它强调了一个事实,即值不必是连续的。
rhavin

另外,如果您想要稀疏(或重复)的整数值集,而不是使用0 ..(count-1)的默认序数,则这似乎是唯一可行的答案。如果您要与现有代码交互(例如通过网络),那可能很重要。
benkc

值得指出的是,与上述答案一样,将values()的结果缓存起来可能是值得的,这样可以避免每次调用时分配内存和进行数组复制。
benkc

1
并且,根据枚举的长度,您可能想创建一个HashMap或使用二进制搜索或其他方式进行此查找,而不是每次都执行线性搜索。
benkc

2
这应该是将int转换为枚举的正确方法和最佳实践,我认为您可以通过公共静态A GetValue(int _id){for(A a:A.values(){if(a.getId( )== _ id){return a;}}返回null;}摆脱None,isEmpty()和compare()的东西
Chris.Zou 2014年

35

您可以这样尝试。
使用元素ID创建类。

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

现在,使用id作为int来获取此Enum。

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
这是我现在使用的解决方案。但是恕我直言,如果您不给该字段values提供与方法相同的名称,它将减少混乱values()。我使用cachedValues字段名称。
制造商史蒂夫(Steve)2015年

2
高效典雅;复制并粘贴到我的项目中:)我唯一更改的是fromInt(int i),我将其简称为from(int i),因为在签名中具有两次int有点多余。
pipedreambomb

1
为什么不从头开始初始化?为什么要等待第一击?
kaiser

9

Java枚举没有在C ++中进行的那种枚举到整数的映射。

也就是说,所有枚举都有一个values返回可能的枚举值数组的方法,因此

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

应该管用。有点讨厌,最好不要尝试将ints 转换为Enums(反之亦然)。


9

这不是通常要做的事情,所以我会重新考虑。但是,基本的操作是:int->使用EnumType.values()[intNum]的枚举,以及enum->使用enumInst.ordinal()的int。

但是,由于values()的任何实现都别无选择,只能给您一个数组的副本(java数组绝不是只读的),因此最好使用EnumMap来缓存enum-> int映射。


2
关于“这不是通常要做的事情”:有用的常见情况:枚举对应于存储在数据库中的int值。
制造商

@ToolmakerSteve,您对映射是绝对正确的。但是您是否想将这种编码留给某些OR映射器或某些工具包/库?
Dilum Ranatunga


6

这是我计划使用的解决方案。这不仅适用于非顺序整数,而且还应适用于您可能希望用作枚举值的基础ID的任何其他数据类型。

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

我只需要在特定时间(从文件加载数据时)将id转换为枚举,所以没有理由让我始终将Map保留在内存中。如果确实需要始终可访问该映射,则可以始终将其作为Enum类的静态成员进行缓存。


恕我直言,如果我担心内存使用情况,我会动态创建HashMap-类似于@ossys,但是当缓存为null时使用不同的代码,然后在使用完后添加第二种方法clearCachedValues(将私有字段设置为null)。我认为MyEnum.fromInt(i)比绕过地图对象更容易理解。
制造商史蒂夫(Steve)2015年

6

为了帮助其他人,我更喜欢的选项(未在此处列出)使用了Guava的Maps功能

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 IllegalArgumentExceptionfromInt可以返回Optional,无论您喜欢哪种行为。


3
您应该提到您正在使用番石榴。或者您可以使用流:Map<Integer, MyEnum> reverseLookup = Arrays.stream(MyEnum.values()).collect(Collectors.toMap(MyEnum::getValue, Function.identity()));
shmosel

1
也可能要定义一个getValue()方法。
shmosel

@shmosel糟糕,我错过了getValue函数,谢谢。将通用版本键入stackoverflow并不总是会成功。添加了有关通过链接使用番石榴的评论。首选番石榴方法而不是流方法。
乍得·贝弗斯

3

您可以遍历values()枚举并将枚举的整数值与给定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

一个不错的选择是避免从转换intenum:例如,如果需要最大值,则可以将x.ordinal()与y.ordinal()比较,并相应地返回x或y。(您可能需要重新排序值,以使这种比较有意义。)

如果那不可能,我将存储MyEnum.values()到静态数组中。


1
如果从数据库获取int和希望将其转换成枚举,我认为这是一个很常见的任务,你需要INT - >枚举转换,IMO ..
井上雄几

0

这是与医生相同的答案,但它显示了如何消除可变数组的问题。如果您首先由于分支预测而使用这种方法,则效果几乎没有到零,并且整个代码仅调用一次可变数组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;
}

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我认为,如果越界越少,则异常会好得多。
Zoso

您的信念基于什么?
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;
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.