在 Java 中将 Int 转换为枚举

Cast Int to enum in Java

提问人:Maxim Gershkovich 提问时间:5/4/2011 最后编辑:trumpetlicksMaxim Gershkovich 更新时间:5/13/2021 访问量:345878

问:

给定以下枚举,在 Java 中将 Int 转换为枚举的正确方法是什么?

public enum MyEnum
{
    EnumValue1,
    EnumValue2
}


MyEnum enumValue = (MyEnum) x; //Doesn't work???
Java 强制转换 枚举序

评论


答:

650赞 Thomas 5/4/2011 #1

尝试 where must be 或 ,即该枚举的有效序号。MyEnum.values()[x]x01

请注意,在 Java 中,枚举实际上是类(因此枚举值是对象),因此您不能将 or 转换为枚举。intInteger

评论

127赞 Peter Lawrey 5/4/2011
+1:您可能希望缓存,因为它很昂贵。也就是说,如果你叫它数百次。MyEnum.values()
7赞 Tarrasch 12/4/2012
@PeterLawrey 为了完整起见,你能解释一下为什么它应该很慢吗?我看不出有明显的原因。
54赞 Peter Lawrey 12/4/2012
@Tarrasch由于数组是可变的,values() 必须返回元素数组的副本,以防万一您碰巧更改它。每次创建此副本的成本相对较高。
9赞 Tarrasch 12/5/2012
@PeterLawrey我最近一直在使用很多 haskell(一切都是不可变的)!感谢您清晰简洁的解释。:)
0赞 Toofy 10/5/2022
如果使用 spring 是“缓存”的好方法,那么只需将枚举声明为某处 Configuration 类中的单例 bean。
8赞 w.donahue 5/4/2011 #2

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

12赞 Cameron Skinner 5/4/2011 #3

Java 枚举没有与 C++ 中相同的枚举到 int 映射。

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

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

应该工作。这有点令人讨厌,如果可能的话,最好不要尝试从 s 转换为 s(反之亦然)。intEnum

10赞 Dilum Ranatunga 5/4/2011 #4

这不是通常做的事情,所以我会重新考虑。但话虽如此,基本操作是:int --> enum using 和 enum --> int using 。EnumType.values()[intNum]enumInst.ordinal()

但是,由于任何实现都别无选择,只能为您提供数组的副本(java 数组从来都不是只读的),因此最好使用 an 来缓存枚举 --> int 映射。values()EnumMap

评论

3赞 ToolmakerSteve 6/27/2015
Re “这不是通常做的事情”:它有用的常见情况:枚举对应于存储在数据库中的 int 值。
0赞 Dilum Ranatunga 8/19/2015
@ToolmakerSteve您绝对正确地认为映射是必需的。但是你想把这种编码留给一些O-R映射器或一些工具包/库吗?
190赞 Lorenzo Polidori 1/7/2012 #5

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

评论

53赞 Gili Nachum 6/5/2012
如果你想避免开关维护,那么在使用类上: private final MyEnum[] myEnumValues = MyEnum.values();然后用法:myEnum = myEnumValues[i];
26赞 Dandre Allison 1/9/2013
@GiliNachum以一种奇怪的方式说出来,但这个解决方案的问题在于可维护性。它违背了 DRY 原则,这意味着每当枚举值发生更改(重新排序、添加值、删除值)时,switch 语句必须同时更新。Gili 的注释迫使 Java 保持与枚举值列表的一致性,对枚举值的更改根本不会影响这种方法。
3赞 brunsgaard 10/5/2013
@LorenzoPolidori,您能解释一下为什么您认为这是一项昂贵的手术吗?我不知道它是如何详细工作的,但对我来说,访问数组中的元素似乎没什么大不了的,也就是恒定时间。如果必须构建阵列,则需要 O(n) 时间,这与解决方案的运行时间相同。MyEnum.values()[x]
2赞 MikeFHay 1/25/2014
@brunsgaard我假设每次都会生成一个新数组,因为数组是可变的,因此多次返回相同的数组是不安全的。switch 语句不一定是 O(n),它们可以编译为跳转表。因此,洛伦佐的说法似乎是有道理的。values()
1赞 Dandre Allison 2/10/2020
@Johnson Gili 的版本不需要除了 Enum 声明中的更改之外进行任何其他代码更改。如果将新项添加到枚举中,则仍将返回枚举中的第 1 项,而不进行更改。myEnum = myEnumValues[i]i
0赞 18446744073709551615 7/2/2012 #6

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

如果这是不可能的,我会存储到一个静态数组中。MyEnum.values()

评论

2赞 Yuki Inoue 12/2/2018
如果您从 DB 获取 int 并想将其转换为枚举,我认为这是一项非常常见的任务,您将需要 int -> 枚举转换,IMO。
22赞 ossys 12/19/2012 #7

我缓存了这些值并创建了一个简单的静态访问方法:

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赞 ToolmakerSteve 6/27/2015
这是我现在使用的解决方案。但是恕我直言,如果您不为该字段提供与方法相同的名称,则不会那么令人困惑。我用于字段名称。valuesvalues()cachedValues
3赞 pipedreambomb 5/19/2017
高效优雅;复制并粘贴到我的项目中:)我唯一更改的是 fromInt(int i),我只是调用 from(int i),因为在签名中包含两次 int 有点多余。
1赞 kaiser 2/16/2018
为什么不从头开始初始化呢?为什么要等待第一次命中?
0赞 mnsc 1/18/2023
@kaiser没有地方可以“从头开始”获取 values()。枚举构造函数是按枚举值计算的,因此在这些构造函数中给出 -java.lang.NullPointerException: Cannot invoke “[LFooBar;.clone()“,因为”FooBar.$VALUES“为空this.values()
0赞 mnsc 1/18/2023
我认为这可能是一个更好的名字,因为有很多替代解决方案,例如 @Doctor 的答案,它们使用合成/显式 int 值,例如 或 .fromOrdinal(int)idvalue
54赞 Doctor 1/8/2013 #8

如果要给出整数值,可以使用如下所示的结构

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

评论

7赞 rhavin 2/7/2013
+1,因为它突出了一个事实,即值不必是连续的。
0赞 benkc 4/23/2013
此外,如果您想要一组稀疏(或重复)的整数值,而不是使用从 0 开始的默认序号,这似乎是唯一有效的答案。(计数-1)。如果要与现有代码交互(例如通过网络),这可能很重要。
0赞 benkc 4/23/2013
值得指出的是,如上所述,缓存 values() 的结果可能是值得的,这样可以避免每次调用它时都进行内存分配和数组复制。
1赞 benkc 4/23/2013
而且,根据枚举的长度,您可能希望创建 HashMap 或使用二进制搜索或其他方式进行此查找,而不是每次都进行线性搜索。
2赞 Chris.Zou 5/13/2014
这应该是将 int 转换为枚举的正确方法和最佳实践,我认为您可以通过公共静态 A GetValue(int _id) { for(A a:A.values() { if(a.getId()==_id) { return a; }} return null; }去掉 None、isEmpty() 和 compare() 的东西。
0赞 Ali Akdurak 5/23/2013 #9

这与医生的答案相同,但它显示了如何使用可变数组消除问题。如果由于分支预测而使用这种方法,则 if 的影响很小甚至为零,并且整个代码只调用可变数组 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;
}
6赞 jkindwall 2/9/2015 #10

这是我计划采用的解决方案。这不仅适用于非序列整数,还适用于您可能希望用作枚举值的基础 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 类的静态成员。

评论

0赞 ToolmakerSteve 6/27/2015
恕我直言,如果我担心内存使用,我会动态创建 HashMap - 就像 @ossys 一样,但在缓存为 null 时使用不同的代码,然后在使用完它后添加第二个方法(将私有字段设置回 null)。我认为比传递地图对象更容易理解。clearCachedValuesMyEnum.fromInt(i)
53赞 Piyush Ghediya 11/3/2015 #11

你可以试试这样。
使用元素 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 获取此枚举。

MyEnum myEnum = MyEnum.fromId(5);
0赞 Zoso 12/15/2015 #12
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;
        }
    }
}

评论

0赞 Zoso 11/17/2017
@shmosel我相信,如果越界相对较少,例外情况会好得多。
0赞 shmosel 11/18/2017
你的信念基于什么?
3赞 Gerrit Brink 4/7/2016 #13

编写了此实现。它允许缺失值、负值并保持代码一致性。地图也会被缓存。使用接口并需要 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;
    }
}

评论

0赞 deepakl.2000 7/27/2023
评论 :: 你能不能写代码注释,让开发者理解分步逻辑
4赞 Yashar Aliabbasi 6/20/2017 #14

您可以遍历枚举并将枚举的整数值与给定值进行比较,如下所示: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

6赞 Chad Befus 11/16/2017 #15

如果它对其他人有帮助,我更喜欢的选项(此处未列出)使用 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);
    }
}

使用默认值,您可以使用 ,您可以或您可以返回 ,无论您喜欢什么行为。nullthrow IllegalArgumentExceptionfromIntOptional

评论

4赞 shmosel 11/16/2017
你应该提到你正在使用番石榴。或者,您可以使用流:Map<Integer, MyEnum> reverseLookup = Arrays.stream(MyEnum.values()).collect(Collectors.toMap(MyEnum::getValue, Function.identity()));
1赞 shmosel 11/16/2017
可能还想定义一个方法。getValue()
0赞 Chad Befus 11/16/2017
@shmosel 哎呀,我错过了getValue函数,谢谢。在 stackoverflow 中键入通用版本并不总是成功。添加了有关使用带有链接的番石榴的评论。首选番石榴方法而不是流。
6赞 Yuki Inoue 12/2/2018 #16

根据 @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赞 deepakl.2000 12/17/2022
这是否适用于双倍,短,浮点,多头
0赞 Mark Douglas 6/5/2023
@deepakl.2000 : 枚举值是整数值,因此仅支持 int 是有效的。例如,我认为不可能有一个具有浮点序数的 Enum。
1赞 CoolMind 3/12/2019 #17

在 Kotlin 中:

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