【问题标题】:Convert integer value to matching Java Enum将整数值转换为匹配的 Java 枚举
【发布时间】:2011-07-14 15:33:57
【问题描述】:

我有一个这样的枚举:

public enum PcapLinkType {
  DLT_NULL(0)
  DLT_EN10MB(1)
  DLT_EN3MB(2),
  DLT_AX25(3),
  /*snip, 200 more enums, not always consecutive.*/
  DLT_UNKNOWN(-1);
    private final int value;   

    PcapLinkType(int value) {
        this.value= value;
    }
}

现在我从外部输入获得一个 int 并想要匹配的输入 - 如果值不存在则抛出异常是可以的,但在这种情况下我最好将它设置为 DLT_UNKNOWN

int val = in.readInt();
PcapLinkType type = ???; /*convert val to a PcapLinkType */

【问题讨论】:

    标签: java


    【解决方案1】:

    您必须创建一个新的静态方法,在其中迭代 PcapLinkType.values() 并进行比较:

    public static PcapLinkType forCode(int code) {
        for (PcapLinkType typе : PcapLinkType.values()) {
            if (type.getValue() == code) {
                return type;
            }
        }
        return null;
     }
    

    如果很少调用它就可以了。如果被频繁调用,那就看别人建议的Map优化。

    【讨论】:

    • 如果调用很多可能会很昂贵。构建静态地图可能会提供更好的摊销成本。
    • @dty o(n) with n = 200 - 我不认为这是个问题
    • 这是一个完全荒谬的声明,不知道它被调用的频率。如果它被调用一次,很好。如果 10Ge 网络上的每个数据包都需要调用它,那么使算法速度提高 200 倍是非常重要的。因此,为什么我用“如果经常调用”来限定我的陈述
    【解决方案2】:

    您需要手动执行此操作,方法是在将整数映射到枚举的类中添加一个静态映射,例如

    private static final Map<Integer, PcapLinkType> intToTypeMap = new HashMap<Integer, PcapLinkType>();
    static {
        for (PcapLinkType type : PcapLinkType.values()) {
            intToTypeMap.put(type.value, type);
        }
    }
    
    public static PcapLinkType fromInt(int i) {
        PcapLinkType type = intToTypeMap.get(Integer.valueOf(i));
        if (type == null) 
            return PcapLinkType.DLT_UNKNOWN;
        return type;
    }
    

    【讨论】:

    • 根据 dty 的建议进行了更新,这是个好主意。
    • 我希望你先通过编译器运行我的代码......我只是想出来了。我知道这项技术有效——我昨天用过。但是代码在另一台机器上,而这台机器没有我的开发工具。
    • allOf 仅适用于集合
    • 另外,EnumMap 使用枚举作为键。在这种情况下,OP 希望枚举作为值。
    • 这似乎是很多不必要的开销。那些真正需要这种类型操作的人可能需要高性能,因为他们正在从流/套接字写入/读取,在这种情况下,values() 的缓存(如果您的枚举值是连续的)或简单的switch声明将轻松击败这种方法。如果您的Enum 中只有少数条目,那么仅仅为了不必更新switch 语句的方便而添加HashMap 的开销就没有多大意义。这种方法可能看起来更优雅,但也很浪费。
    【解决方案3】:

    正如@MeBigFatGuy 所说,除了你可以让你的static {...} 块在values() 集合上使用循环:

    static {
        for (PcapLinkType type : PcapLinkType.values()) {
            intToTypeMap.put(type.getValue(), type);
        }
    }
    

    【讨论】:

      【解决方案4】:
      static final PcapLinkType[] values  = { DLT_NULL, DLT_EN10MB, DLT_EN3MB, null ...}    
      
      ...
      
      public static PcapLinkType  getPcapLinkTypeForInt(int num){    
          try{    
             return values[int];    
          }catch(ArrayIndexOutOfBoundsException e){    
             return DLT_UKNOWN;    
          }    
      }    
      

      【讨论】:

      • 如果调用很多的话会很贵。需要记住更新数组(为什么在枚举定义.values() 方法时还要更新数组?)。
      • @dty 是 try/catch 吗?如果很多值都属于 DLT_UNKNOWN 类别,我认为说它很贵会更公平。
      • 看到数组解决方案被否决而地图解决方案被投赞成票,我真的很惊讶。我不喜欢这里的是--int,但这显然是一个错字。
      • 我明白了:他们想要 null 代替 DLT_UKNOWN :)
      • 为什么不static final values[] = PcapLinkType.values()
      【解决方案5】:

      您可以在枚举中添加一个静态方法,该方法接受 int 作为参数并返回 PcapLinkType

      public static PcapLinkType of(int linkType) {
      
          switch (linkType) {
              case -1: return DLT_UNKNOWN
              case 0: return DLT_NULL;
      
              //ETC....
      
              default: return null;
      
          }
      }
      

      【讨论】:

      • 如果你添加一个新的枚举,最好不要忘记在 switch 语句中添加一个条目。不理想,恕我直言。
      • @dty 那么,您认为 HashMap 的开销超过了在 switch 语句中添加新案例的需要吗?
      • 在我专注于哈希查找的微观性能之前,我想我更愿意编写有助于我不犯错误并因此更可能是正确的代码。
      【解决方案6】:

      你可以做这样的事情来自动将它们全部注册到一个集合中,然后可以轻松地将整数转换为相应的枚举。 (顺便说一句,在枚举构造函数 is not allowed 中将它们添加到映射中。即使在使用 Java 多年后学习新事物也很不错。:)

      public enum PcapLinkType {
          DLT_NULL(0),
          DLT_EN10MB(1),
          DLT_EN3MB(2),
          DLT_AX25(3),
          /*snip, 200 more enums, not always consecutive.*/
          DLT_UNKNOWN(-1);
      
          private static final Map<Integer, PcapLinkType> typesByValue = new HashMap<Integer, PcapLinkType>();
      
          static {
              for (PcapLinkType type : PcapLinkType.values()) {
                  typesByValue.put(type.value, type);
              }
          }
      
          private final int value;
      
          private PcapLinkType(int value) {
              this.value = value;
          }
      
          public static PcapLinkType forValue(int value) {
              return typesByValue.get(value);
          }
      }
      

      【讨论】:

      • 这就是您在发布之前仔细检查您的答案所得到的。 ;)
      【解决方案7】:

      这是我用的:

      public enum Quality {ENOUGH,BETTER,BEST;
                           private static final int amount = EnumSet.allOf(Quality.class).size();
                           private static Quality[] val = new Quality[amount];
                           static{ for(Quality q:EnumSet.allOf(Quality.class)){ val[q.ordinal()]=q; } }
                           public static Quality fromInt(int i) { return val[i]; }
                           public Quality next() { return fromInt((ordinal()+1)%amount); }
                          }
      

      【讨论】:

      • 使用序数已被确定为不好的做法,一般来说,最好避免。
      【解决方案8】:

      有一个静态方法 values() 记录在案,但不是您所期望的:http://docs.oracle.com/javase/tutorial/java/javaOO/enum.html

      enum MyEnum {
          FIRST, SECOND, THIRD;
          private static MyEnum[] allValues = values();
          public static MyEnum fromOrdinal(int n) {return allValues[n];}
      }
      

      原则上,您可以只使用values()[i],但有传言称values() 会在每次调用时创建数组的副本。

      【讨论】:

      • 根据 Joshua Bloch (Effective Java Book)永远不要从枚举的序数中派生与枚举相关的值;您的实现不应依赖在枚举顺序上。
      • 实现什么?如果我们实现某种算法,实现不应该依赖枚举顺序除非该顺序被记录。当我们实现枚举本身时,可以使用这样的实现细节,就像使用类私有方法一样。
      • 不同意。我相信 never 意味着不管文档。即使您自己实现枚举,也不应该使用序数。这是一种难闻的气味,而且容易出错。我不是专家,但我不会与 Joshua Bloch 争论 :)
      • @stevo.mit 看看 Java 8 中的新枚举 java.time.Month。Month.of(int) 静态方法正是 Joshua Bloch 所说的你应该“从不”做的事情。它根据其序数返回一个月份。
      • @stevo.mit 有有序枚举无序枚举。 (还有位掩码枚举。)将它们仅仅称为“枚举”是不正确的。使用何种表达方式的决定必须基于您所从事的抽象级别。使用实现细节(来自较低级别的表达方式)或使用假设(来自更高级别的表达方式)确实是不正确的。至于“never”,在人类语言中,never never 表示从不,因为总有一些上下文。 (通常,在应用程序编程中,从不...)顺便说一句,programering.com/a/MzNxQjMwATM.html
      【解决方案9】:

      如果你有这样的枚举

      public enum PcapLinkType {
        DLT_NULL(0)
        DLT_EN10MB(1)
        DLT_EN3MB(2),
        DLT_AX25(3),
        DLT_UNKNOWN(-1);
      
          private final int value;   
      
          PcapLinkType(int value) {
              this.value= value;
          }
      }
      

      那么你可以像这样使用它

      PcapLinkType type = PcapLinkType.values()[1]; /*convert val to a PcapLinkType */
      

      【讨论】:

      • 你错过了评论 /*snip,还有 200 个枚举,并不总是连续的。*/
      • 以防您的枚举值是从零开始的传递性,这是不好的做法
      【解决方案10】:

      我知道这个问题已经有几年的历史了,但与此同时,Java 8 给我们带来了Optional,我想我会提供一个使用它的解决方案(以及StreamCollectors) :

      public enum PcapLinkType {
        DLT_NULL(0),
        DLT_EN3MB(2),
        DLT_AX25(3),
        /*snip, 200 more enums, not always consecutive.*/
        // DLT_UNKNOWN(-1); // <--- NO LONGER NEEDED
      
        private final int value;
        private PcapLinkType(int value) { this.value = value; }
      
        private static final Map<Integer, PcapLinkType> map;
        static {
          map = Arrays.stream(values())
              .collect(Collectors.toMap(e -> e.value, e -> e));
        }
      
        public static Optional<PcapLinkType> fromInt(int value) {
          return Optional.ofNullable(map.get(value));
        }
      }
      

      Optional 类似于null:它表示没有(有效)值的情况。但它是nullDLT_UNKNOWN 等默认值的更类型安全的替代方案,因为您可能忘记检查nullDLT_UNKNOWN 情况。它们都是有效的PcapLinkType 值!相反,您不能将Optional&lt;PcapLinkType&gt; 值分配给PcapLinkType 类型的变量。 Optional 让您首先检查有效值。

      当然,如果您想保留DLT_UNKNOWN 以实现向后兼容性或其他任何原因,即使在这种情况下,您仍然可以使用Optional,使用orElse() 将其指定为默认值:

      public enum PcapLinkType {
        DLT_NULL(0),
        DLT_EN3MB(2),
        DLT_AX25(3),
        /*snip, 200 more enums, not always consecutive.*/
        DLT_UNKNOWN(-1);
      
        private final int value;
        private PcapLinkType(int value) { this.value = value; }
      
        private static final Map<Integer, PcapLinkType> map;
        static {
          map = Arrays.stream(values())
              .collect(Collectors.toMap(e -> e.value, e -> e));
        }
      
        public static PcapLinkType fromInt(int value) {
          return Optional.ofNullable(map.get(value)).orElse(DLT_UNKNOWN);
        }
      }
      

      【讨论】:

        【解决方案11】:

        没有办法优雅地处理基于整数的枚举类型。您可能会考虑使用基于字符串的枚举来代替您的解决方案。不是一直首选的方式,但它仍然存在。

        public enum Port {
          /**
           * The default port for the push server.
           */
          DEFAULT("443"),
        
          /**
           * The alternative port that can be used to bypass firewall checks
           * made to the default <i>HTTPS</i> port.
           */
          ALTERNATIVE("2197");
        
          private final String portString;
        
          Port(final String portString) {
            this.portString = portString;
          }
        
          /**
           * Returns the port for given {@link Port} enumeration value.
           * @return The port of the push server host.
           */
          public Integer toInteger() {
            return Integer.parseInt(portString);
          }
        }
        

        【讨论】:

          【解决方案12】:

          这可能不是一个很好的解决方案,但它对我有用:

          public enum Type {
                  WATER, FIRE, GRASS;
          
                  public static Type getType(int value){
                      if(value==WATER.ordinal()){
                          return WATER;
                      }else if(value==FIRE.ordinal()){
                          return FIRE;
                      }else if(value==GRASS.ordinal()){
                          return GRASS;
                      }else {
                          return null;
                      }
                  }
              }
          

          【讨论】:

          • 请解释你的代码是做什么的以及它是怎么做的。
          • .ordinal() 检索枚举在枚举列表中的位置的最终 int 值。根据 ORACLE:“返回此枚举常量的序数(它在其枚举声明中的位置,其中初始常量的序数为零)。” docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/…
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-03-17
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多