在Java中将Int转换为枚举类型

386
在Java中,将Int转换为枚举的正确方法是什么?给定以下枚举要怎么做?
public enum MyEnum
{
    EnumValue1,
    EnumValue2
}


MyEnum enumValue = (MyEnum) x; //Doesn't work???
17个回答

650

尝试使用 MyEnum.values()[x],其中x必须是01,即为该枚举的有效序数。

请注意,在Java中枚举实际上是类(枚举值也因此是对象),因此您不能将int甚至Integer强制转换为枚举。


127
如果您需要多次调用MyEnum.values(),可以将其缓存起来以节省开销。即使这个操作很耗时。 - Peter Lawrey
7
为了完整起见,你能解释一下为什么它应该很慢吗?我没有看到明显的原因。 - Tarrasch
54
由于数组是可变的,因此values()必须返回元素数组的副本,以防您更改它。每次创建此副本都相对昂贵。 - Peter Lawrey
9
@PeterLawrey 最近我一直在使用 Haskell(其中一切都是不可变的)!感谢你清晰简洁的解释。 :) - Tarrasch
如何获取 'x'? - Beeing Jk
显示剩余3条评论

190

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
如果您想避免切换维护,那么在使用类中添加以下代码:
private final MyEnum[] myEnumValues = MyEnum.values();
然后在使用时调用:
myEnum = myEnumValues[i];
- Gili Nachum
26
@GiliNachum的措辞有些奇怪,但这种解决方案的问题在于可维护性。它违反了DRY原则,这意味着无论是重新排序枚举值、添加或删除值,switch语句都必须同时更新。Gili的评论强制Java与枚举值列表保持一致,对枚举值的更改不会影响该方法。 - Dandre Allison
3
@LorenzoPolidori,您能解释一下为什么您认为MyEnum.values()[x]是一个昂贵的操作吗?我不知道它的详细工作原理,但对我来说,似乎访问数组中的元素并不是什么大问题,即常数时间。如果需要构建数组,则需要O(n)时间,这与您的解决方案具有相同的运行时间。 - brunsgaard
2
@brunsgaard 我假设 values() 每次都会生成一个新的数组,因为数组是可变的,所以多次返回同一个数组可能不安全。switch语句不一定是O(n),它们可以被编译成跳转表。所以Lorenzo的说法似乎是有道理的。 - MikeFHay
1
@Johnson Gili的版本不需要在枚举声明之外进行任何额外的代码更改。如果您向枚举中添加新项,则myEnum = myEnumValues[i]仍将返回枚举中的第i项,而无需更改。 - Dandre Allison
显示剩余7条评论

54

如果你想给整数赋值,可以使用以下结构

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
+1 是因为它突显了一个事实,即数值不必连续。 - rhavin
1
根据你的枚举长度,你可能想要创建一个HashMap或使用二分查找等方式进行查找,而不是每次进行线性搜索。 - benkc
2
这应该是将int转换为enum的正确方式和最佳实践,我认为你可以通过public static A GetValue(int _id)来简化问题。 { for(A a:A.values() { if(a.getId()==_id) { return a; }} return null; } 摆脱None、isEmpty()和compare()等内容。 - Chris.Zou
这可能有效,但在我看来,它太容易被误用了。如果假设一个“正常”的枚举并执行A.values()[ some_id ];,当你传递11时,它当然不会返回None - 463035818_is_not_a_number
能否像这样设置一个级别,然后编写代码 B(level>0 && level<10)C(level>10 && level<20)......请帮助我,因为我在Java 8中有以下要求,根据条件输出B或输出C。我已经卡了很长时间,请帮忙。 - deepakl.2000
显示剩余2条评论

53

您可以尝试这样做。
创建具有元素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获取此枚举。

MyEnum myEnum = MyEnum.fromId(5);

22

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

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”作为字段名称。 - ToolmakerSteve
3
高效且优雅;复制并粘贴到我的项目中 :) 我所做的唯一更改是 fromInt(int i) 方法,我只是将其改称为 from(int i),因为在方法签名中两次出现 int 有点多余。 - pipedreambomb
1
为什么不从一开始就初始化呢?为什么要等待第一次命中? - kaiser
@kaiser,没有“从开始”获取values()的地方。枚举构造函数是按枚举值区分的,因此在这些构造函数中,this.values()会导致-_java.lang.NullPointerException: Cannot invoke "[LFooBar;.clone()" because "FooBar.$VALUES" is null_。 - mnsc
我认为 fromOrdinal(int) 可能是一个更好的名称,因为有很多替代方案,比如 @Doctor 在这里提供的使用合成/显式 int 值的答案,比如 idvalue - mnsc

12

Java枚举类型没有像C++中那样的枚举到整数映射。

话虽如此,所有的枚举类型都有一个values方法,用于返回可能的枚举值数组,因此

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

应该可以工作。虽然有点糟糕,但如果可能的话最好不要尝试将int转换为Enum(反之亦然)。


10

这并不是通常做的事情,所以我建议您重新考虑。但是话说回来,基本操作是:使用EnumType.values()[intNum]将int转为枚举,使用enumInst.ordinal()将枚举转为int。

然而,由于任何values()方法的实现都只会给你一个数组的副本(Java 数组永远不会是只读的),因此最好使用EnumMap来缓存枚举--> int 映射。


3
“这不是通常做的事情”:适用的常见情况是,枚举对应于存储在数据库中的整数值。 - ToolmakerSteve
@ToolmakerSteve,你绝对是对的,需要这些映射。但是你会想要把这种编码工作留给某个 O-R 映射器或者工具库吗? - Dilum Ranatunga

8

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


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一样,只是当缓存为空时使用不同的代码,然后在使用完毕后添加第二个方法clearCachedValues(将私有字段设置回null)。我认为MyEnum.fromInt(i)比传递映射对象更容易理解。 - ToolmakerSteve

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 IllegalArgumentException 或者您的 fromInt 可以返回一个 Optional,无论您喜欢哪种行为。


4
你应该提到你正在使用Guava库。或者你可以使用Java 8的Streams API:Map<Integer, MyEnum> reverseLookup = Arrays.stream(MyEnum.values()).collect(Collectors.toMap(MyEnum::getValue, Function.identity())); - shmosel
1
可能还需要定义一个 getValue() 方法。 - shmosel
@shmosel 哎呀,我错过了getValue函数,谢谢。在stackoverflow上输入通用版本并不总是奏效的。添加了关于使用Guava的注释和链接。更喜欢使用Guava方法而不是streams。 - Chad Befus

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接