将整数值转换为匹配的Java枚举


85

我有一个这样的枚举:

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 */

Answers:


104

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

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

1
更新了dty的建议,这是个好主意。
MeBigFatGuy 2011年

希望您首先通过编译器运行我的代码...我只是想尽办法了。我知道这项技术有效-我昨天用过。但是代码在另一台机器上,而这台机器没有我的开发工具。
dty

1
allOf仅适用于套装
MeBigFatGuy 2011年

1
另外,EnumMap将枚举用作键。在这种情况下,OP希望枚举作为值。
dty

8
这似乎是很多不必要的开销。那些实际上需要这种类型的操作的人可能需要高性能,因为他们正在从流/套接字中进行写入/读取,在这种情况下,缓存values()(如果您的枚举值是顺序的)或简单的switch语句将方便地击败该方法。 。如果您只有几个条目,Enum那么仅仅为了不必更新switch语句而增加HashMap的开销就没有多大意义。这种方法看似更优雅,但也很浪费。
2013年

30

有一个静态方法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];}
}

原则上,您可以使用just values()[i],但是有传言说values()每次调用该数组都会创建该数组的副本。


9
根据Joshua Bloch (有效的Java书籍)所述永远不要从其序数中得出与枚举关联的值;您的实现不应依赖于枚举顺序。
stevo.mit 2013年

4
执行什么?如果我们实现某种算法,那么除非记录了该顺序,否则实现不应依赖于枚举顺序。当我们实现枚举本身时,可以使用此类实现细节,就像可以使用类私有方法一样。
18446744073709551615

1
不同意 我相信,无论文档如何,都绝不意味着。即使自己实现枚举,也不应使用序数。这是难闻的气味,容易出错。我不是专家,但我不会与Joshua Bloch争论:)
stevo.mit 2013年

4
@ stevo.mit看看Java 8中新的枚举java.time.Month。Month.of(int)静态方法完全执行Joshua Bloch所说的“绝不”做的事情。它根据顺序返回一个月。
Klitos Kyriacou 2015年

1
@ stevo.mit有有序的枚举无序的枚举。(还有位掩码枚举。)将它们仅称为“枚举”是完全不正确的。使用哪种表达方式的决定必须基于您所从事的抽象水平。使用实现细节(较低层级的表达方式)或使用假设(较高层级的表达方式)确实是不正确的。关于“从不”,在人类语言中,从不永远不会意味着永远,因为总是有某种上下文。(通常,在应用程序编程中,从不...)BTW, programering.com
a / MzNxQjMwATM.html

14

您将不得不在迭代PcapLinkType.values()并进行比较的地方创建一个新的静态方法:

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

如果很少调用它会很好。如果经常调用它,请查看Map其他人建议的优化。


4
如果叫很多,可能会很昂贵。构建静态地图可能会带来更好的摊销成本。
dty

@dty o(n)的n = 200-我不认为这是一个问题
Bozho 2011年

7
那是一个完全荒谬的声明,没有感觉它被调用的频率。如果调用一次,那就好。如果要求每个数据包在10Ge网络上回荡,那么使算法快200倍非常重要。因此,为什么我用“如果叫很多”来限定我的陈述
dty

10

您可以执行以下操作将它们全部自动注册到一个集合中,然后可以使用它们轻松地将整数转换为相应的枚举。(顺便说一句,不允许将它们添加到enum构造函数中的映射中。即使经过多年使用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);
    }
}

1
这就是您在发布之前仔细检查答案的结果。;)
Esko Luontola 2011年

10

如果你有这样的枚举

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个枚举,并不总是连续的。* /
MeBigFatGuy 2015年

万一您的枚举值是从零的可传递性,这是一个不好的做法
cuasodayleo

4

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

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

4

我知道这个问题已有几年历史了,但是与此同时,由于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:表示没有(有效)值的情况。但这是类型安全的替代方法null或默认值,例如,DLT_UNKNOWN因为您可能忘记检查nullorDLT_UNKNOWN情况。它们都是有效值PcapLinkType!相反,您不能将Optional<PcapLinkType>值赋给type变量PcapLinkTypeOptional使您首先检查有效值。

当然要保留 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);
  }
}

3

您可以在枚举中添加一个静态方法,该方法接受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

1
@dty那么,您认为HashMap的开销超过了向switch语句添加新案例的需要吗?
暗恋

1
我想我宁愿编写代码来帮助我避免犯错,因此在我专注于哈希查找的微性能之前,它更有可能是正确的。
2013年

3

这是我用的:

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

通常,最好避免使用序数法。
拉斐尔

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

1
如果打电话很多的话很贵。需要记住要更新数组(为什么在枚举定义.values()方法时为什么还要拥有它?)。
dty

@dty是try / catch吗?我认为,如果很多值都属于DLT_UNKNOWN类别,则说它昂贵就可以了。
nsfyn55 2011年

1
看到阵列解决方案投了反对票,地图解决方案投了赞成票,我感到非常惊讶。我在这里不喜欢的是--int,但是显然是错字。
18446744073709551615'4

我看到:他们想要null代替DLT_UKNOWN:)
18446744073709551615

1
为什么不static final values[] = PcapLinkType.values()呢?
18446744073709551615

0

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

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