【问题标题】:Cast Int to enum in Java将 Int 转换为 Java 中的枚举
【发布时间】:2011-08-18 05:57:43
【问题描述】:

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

public enum MyEnum
{
    EnumValue1,
    EnumValue2
}


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

【问题讨论】:

  • @RyuS。这不是这个问题的重复,

标签: java casting enums ordinal


【解决方案1】:

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

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

【讨论】:

  • Re“这不是通常会做的事情”:有用的常见情况:枚举对应于存储在数据库中的 int 值。
  • @ToolmakerSteve 您完全正确,需要映射。但是你想把这种编码留给一些 O-R 映射器或一些工具包/库吗?
【解决方案2】:

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

【讨论】:

    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

      根据@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());
          }
      }
      

      【讨论】:

        【解决方案5】:

        你可以这样试试。
        创建具有元素 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);
        

        【讨论】:

          【解决方案6】:

          如果对他人有帮助,我更喜​​欢的选项(此处未列出)使用Guava's Maps functionality

          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,无论您喜欢什么行为。

          【讨论】:

          • 你应该提到你正在使用 Guava。或者你可以使用流:Map&lt;Integer, MyEnum&gt; reverseLookup = Arrays.stream(MyEnum.values()).collect(Collectors.toMap(MyEnum::getValue, Function.identity()));
          • 可能还想定义一个getValue() 方法。
          • @shmosel 糟糕,我错过了 getValue 函数,谢谢。在 stackoverflow 中输入通用版本并不总是成功。添加了关于通过链接使用 Guava 的评论。更喜欢 Guava 方法而不是流。
          【解决方案7】:

          您可以遍历枚举的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
          我希望这会有所帮助;)

          【讨论】:

            【解决方案8】:

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

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

            【讨论】:

            • +1:您可能希望缓存 MyEnum.values(),因为它很昂贵。也就是说,如果你调用它数百次。
            • @PeterLawrey 为了完整起见,你能解释一下为什么它应该很慢吗?我看不出有什么明显的原因。
            • @Tarrasch 因为数组是可变的,values() 必须返回元素数组的副本,以防万一您碰巧更改它。每次创建这个副本都比较昂贵。
            • @PeterLawrey 我最近一直在使用很多haskell(一切都是不可变的)!感谢您清晰简洁的解释。 :)
            • 如何得到'x'?
            【解决方案9】:
            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;
                    }
                }
            }
            

            【讨论】:

            【解决方案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 类的静态成员。

            【讨论】:

            • 恕我直言,如果我担心内存使用情况,我会动态创建 HashMap - 与 @ossys 类似,但在缓存为空时使用不同的代码,然后在完成使用后添加第二个方法 clearCachedValues (将私有字段设置回 null)。我认为MyEnum.fromInt(i) 比传递地图对象更容易理解。
            【解决方案11】:

            这与医生的答案相同,但它显示了如何消除可变数组的问题。如果您因为分支预测而使用这种方法,那么 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;
            }
            

            【讨论】:

              【解决方案12】:

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

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

              【讨论】:

              • +1 因为它强调了一个事实,即值不必是连续的。
              • 另外,如果您想要一组稀疏(或重复)的整数值而不是使用 0..(count-1) 中的默认序数,这似乎是唯一可行的答案。如果您正在与现有代码交互(例如通过网络),这可能很重要。
              • 值得指出的是,和上面的答案一样,缓存 values() 的结果可能是值得的,以避免每次调用它时分配内存和 arraycopy。
              • 而且,根据枚举的长度,您可能希望创建一个 HashMap 或使用二进制搜索或其他方式进行此查找,而不是每次都进行线性搜索。
              • 这应该是将 int 转换为 enum 的正确方法和最佳实践,我认为您可以通过 public static A GetValue(int _id) { for(A a:A.values( ) { if(a.getId()==_id) { return a; }} return null; } 去掉 None、isEmpty() 和 compare() 的东西。
              【解决方案13】:

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

              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 作为字段名称。
              • 高效优雅;复制并粘贴到我的项目中:) 我唯一更改的是 fromInt(int i),我只是将其称为 from(int i),因为在签名中包含两次 int 有点多余。
              • 为什么不从头开始初始化?为什么要等待第一次命中?
              【解决方案14】:

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

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

              【讨论】:

              • 如果你从 DB 获取 int 并想将其转换为 Enum,我认为这是一个非常常见的任务,你需要 int -> enum 转换,IMO..
              【解决方案15】:

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

              【讨论】:

              • 如果你想避免开关维护,那么在使用类上:private final MyEnum[] myEnumValues = MyEnum.values();然后用法:myEnum = myEnumValues[i];
              • @GiliNachum 说的很奇怪,但是这个解决方案的问题是可维护性。它违反了 DRY 原则,这意味着无论何时更改枚举值(重新排序、添加值、删除值),都必须同时更新 switch 语句。 Gili 的评论迫使 Java 保持与枚举值列表的一致性,对枚举值的更改根本不会影响这种方法。
              • @LorenzoPolidori,你能解释一下为什么你认为MyEnum.values()[x] 是一项昂贵的操作吗?我不知道它是如何详细工作的,但对我来说,访问 Array 中的元素似乎没什么大不了的,也就是恒定时间。如果必须构建数组,则需要 O(n) 时间,这与您的解决方案的运行时间相同。
              • @brunsgaard 我假设values() 每次都会生成一个新数组,因为数组是可变的,所以多次返回同一个数组是不安全的。 switch语句不一定是O(n),可以编译成跳转表。所以洛伦佐的说法似乎是有道理的。
              • @Johnson Gili 的版本不需要 Enum 声明中的更改之外的任何其他代码更改。如果您向枚举添加新项,myEnum = myEnumValues[i] 仍将返回枚举中的第 i 项而不进行更改。
              【解决方案16】:

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

              【讨论】:

                【解决方案17】:

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

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

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

                应该可以。这有点讨厌,如果可能的话,最好不要尝试从ints 转换为Enums(反之亦然)。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-08-28
                  • 2010-09-06
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多