获取与int值关联的枚举


88

以前,我将LegNo枚举定义为:

NO_LEG, LEG_ONE, LEG_TWO

通过调用return LegNo.values()[i];,我可以获得与每个枚举关联的值。

但是现在我决定让LegNo枚举NO_LEG是int -1而不是0,所以我决定使用私有构造函数进行初始化并设置其int值

NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

private LegNo(final int leg) { legNo = leg; }

现在唯一的事情就是因为我这样做,所以该values()方法不适用于NO_LEG枚举。我如何获得与int相关联的枚举?除了使用case switch语句或if-elseif-elseif之外,还有其他有效的方法吗?

我可以看到很多与从枚举获取int值有关的SO问题,但我反其道而行之。

Answers:


146

编辑2018年8月

今天,我将如下实现

public enum LegNo {
    NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

    private final int value;

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

    public static Optional<LegNo> valueOf(int value) {
        return Arrays.stream(values())
            .filter(legNo -> legNo.value == value)
            .findFirst();
    }
}

您必须在枚举内维护一个映射。

public enum LegNo {
    NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

    private int legNo;

    private static Map<Integer, LegNo> map = new HashMap<Integer, LegNo>();

    static {
        for (LegNo legEnum : LegNo.values()) {
            map.put(legEnum.legNo, legEnum);
        }
    }

    private LegNo(final int leg) { legNo = leg; }

    public static LegNo valueOf(int legNo) {
        return map.get(legNo);
    }
}

静态块将仅被调用一次,因此这里实际上没有性能问题。

编辑:将方法重命名valueOf为与其他Java类更加内联。


对不起,我不确定我是否足够清楚。我想传入int并获取与之关联的枚举。
L-Samuels 2012年

@ L-Samuels猜猜我没有正确阅读您的问题。查看我的更新。
adarshr 2012年

2
我知道这似乎很明显,但可以这样使用:LegNo foo = LegNo.valueOf(2);。先前的代码将返回LegNo.LEG_TWO
FirstOne

1
需要注意的是,传递无效的整数值(未映射)将返回null,这与使用HashMap.get预期的一样:返回指定键所映射到的值;如果此映射不包含该键的映射,则返回null。
FirstOne '17

尽管流语法很简洁,但值得指出的是,它的时间复杂度要比静态映射(公认的具有更高的内存消耗)高。这不是3个值的问题,但如果要valueOf()在另一个循环中使用1000个成员的枚举,则绝对是一个问题。
Patrick M

24

您还可以在枚举中包括一个静态方法,该方法迭代所有成员并返回正确的成员。

public enum LegNo {
   NO_LEG(-1),
   LEG_ONE(1),
   LEG_TWO(2);

   private int legIndex;

   private LegNo(int legIndex) { this.legIndex = legIndex; }

   public static LegNo getLeg(int legIndex) {
      for (LegNo l : LegNo.values()) {
          if (l.legIndex == legIndex) return l;
      }
      throw new IllegalArgumentException("Leg not found. Amputated?");
   }
}

现在,如果要通过整数获取Enum值,则只需使用:

int myLegIndex = 1; //expected : LEG_ONE
LegNo myLeg = LegNo.getLeg(myLegIndex);

我想这比使用if else if语句更为优雅。但是,由于存在更多的枚举搜索,因此@adarshr建议的地图策略会更好。虽然投票支持幽默。
L-Samuels 2012年

1
我也非常喜欢地图策略。尤其是当枚举值很多或必须经常通过此机制查找时。但是,如果通过关联的int查找值的情况比较少见,或者您有很多具有相同查找要求的不同枚举,则我相信我的方法会更节省资源,因为可以节省地图的开销。另外,我发现它可以减少混乱的代码。不过,在一些用例中,我肯定会自己切换到Map类型。
Mike Adler 2012年

永远不要通过序数派生关联的枚举值。使用静态映射是Java架构师推荐的方法。
hfontanez '16

在此示例中,legIndex字段与序数一致,但可以是任何int值。不执行顺序查询。另外,请提供或链接您认为顺序查询不正确的原因。
Mike Adler

1
“找不到腿。截肢了吗?”
Gnagy '17

17

adarshr的答案适用于Java 8:

import static java.util.Arrays.stream;
import static java.util.stream.Collectors.toMap;

import java.util.Map;

public enum LegNo {
    NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

    private final int legNo;

    private final static Map<Integer, LegNo> map =
            stream(LegNo.values()).collect(toMap(leg -> leg.legNo, leg -> leg));

    private LegNo(final int leg) {
        legNo = leg;
    }

    public static LegNo valueOf(int legNo) {
        return map.get(legNo);
    }
}

11

您还可以通过在枚举LegNo上调用values()方法来访问与给定整数值相对应的Enum值。它返回LegNo枚举的字段: LegNo.values()[0]; //returns LEG_NO LegNo.values()[1]; //returns LEG_ONE LegNo.values()[2]; //returns LEG_TWO

并不是他要找的东西,而是非常接近而且确实很简单。(尽管该主题已死,但对其他人可能很有用。)


6

Java 8方式的默认值:

public enum LegNo {
    NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

    private final int legNo;

    LegNo(int legNo) {
        this.legNo = legNo;
    }

    public static LegNo find(int legNo, Supplier<? extends LegNo> byDef) {
        return Arrays.asList(LegNo.values()).stream()
                .filter(e -> e.legNo == legNo).findFirst().orElseGet(byDef);
    }
}

致电:

LegNo res = LegNo.find(0, () -> LegNo.NO_LEG);

或例外:

LegNo res = LegNo.find(0, () -> {
    throw new RuntimeException("No found");
});

1

由于您的枚举只包含3个元素,因此最快的方法就是if else像建议的那样仅使用一系列。

编辑:adarshr提供的答案更适合于具有许多枚举值的一般情况,但是我认为这对于您的问题来说是过大的了。


有一个Map在你的代码是肯定不是矫枉过正。除此之外,该方法比使用if-else条件的意大利面条要干净得多。
adarshr 2012年

我同意,一旦您拥有很多枚举值,Map就会更好,但是对于3个值,我会坚持使用if / else构造。我猜这是一个品味问题。
DieterDP

无论我们选择哪种方法,public LegNo valueOf(int value)都不应更改其方法签名。然后可以将if-else编写在枚举本身内。如果if-else脱离枚举,则它肯定会变成不是那么干净的代码。
adarshr 2012年

1
我完全同意您的意见:)
DieterDP

1
public enum LegNo {
    NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

    private int legNo;

    private LegNo(int leg) { legNo = leg; }

    public static LegNo valueOf(int legNo) {
        for (LegNo leg : LegNo.values()) {
            if (leg.legNo == legNo) return leg;
        }   
    }
}

assert LegNo.valueOf(2) == LegNo.LEG_TWO
assert LegNo.valueOf(3) == null

4
小于10的枚举可接受,但由于查找复杂度O(n),对于大量枚举值完全无效
Alfishe 2014年

1
public enum LegNo {

  NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

  private final int code;

  LegNo(int code) {
    this.code = code;
    ReverseStorage.reverseMap.put(code, this);
  }

  public static Optional<LegNo> getByCode(int code) {
    return Optional.ofNullable(ReverseStorage.reverseMap.get(code));
  }

  private static final class ReverseStorage {
    private static final Map<Integer, LegNo> reverseMap = new LinkedHashMap<>();
  }
}
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.