Java 从枚举序数转换为枚举类型

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/609860/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 16:49:59  来源:igfitidea点击:

Convert from enum ordinal to enum type

javaenums

提问by Lennie

I've the enum type ReportTypeEnumthat get passed between methods in all my classes but I then need to pass this on the URL so I use the ordinal method to get the int value. After I get it in my other JSP page, I need to convert it to back to an ReportTypeEnumso that I can continue passing it.

我有ReportTypeEnum在所有类中的方法之间传递的枚举类型,但是我需要在 URL 上传递它,所以我使用 ordinal 方法来获取 int 值。在我的另一个 JSP 页面中获取它之后,我需要将它转换回 anReportTypeEnum以便我可以继续传递它。

How can I convert ordinal to the ReportTypeEnum?

如何将序数转换为ReportTypeEnum

Using Java 6 SE.

使用 Java 6 SE。

采纳答案by Joachim Sauer

To convert an ordinal into its enum represantation you might want to do this:

要将序数转换为其枚举表示,您可能需要执行以下操作:

ReportTypeEnum value = ReportTypeEnum.values()[ordinal];

Please notice the array bounds.

请注意数组边界。

Note that every call to values()returns a newly cloned array which might impact performance in a negative way. You may want to cache the array if it's going to be called often.

请注意,每次调用都会values()返回一个新克隆的数组,这可能会对性能产生负面影响。如果要经常调用数组,您可能需要缓存它。

Code example on how to cache values().

关于如何缓存values().



This answer was edited to include the feedback given inside the comments

编辑此答案以包含评论中给出的反馈

回答by oxbow_lakes

This is almost certainly a bad idea. Certainly if the ordinal is de-factopersisted (e.g. because someone has bookmarked the URL) - it means that you must always preserve the enumordering in future, which may not be obvious to code maintainers down the line.

这几乎肯定是个坏主意。当然,如果序数实际上是持久的(例如,因为有人将 URL 加入了书签) - 这意味着您enum将来必须始终保留排序,这对于代码维护者来说可能并不明显。

Why not encode the enumusing myEnumValue.name()(and decode via ReportTypeEnum.valueOf(s)) instead?

为什么不编码enumusing myEnumValue.name()(和解码 via ReportTypeEnum.valueOf(s))呢?

回答by Jan

You coulduse a static lookup table:

可以使用静态查找表:

public enum Suit {
  spades, hearts, diamonds, clubs;

  private static final Map<Integer, Suit> lookup = new HashMap<Integer, Suit>();

  static{
    int ordinal = 0;
    for (Suit suit : EnumSet.allOf(Suit.class)) {
      lookup.put(ordinal, suit);
      ordinal+= 1;
    }
  }

  public Suit fromOrdinal(int ordinal) {
    return lookup.get(ordinal);
  }
}

回答by Tony BenBrahim

Every enum has name(), which gives a string with the name of enum member.

每个枚举都有 name(),它给出一个带有枚举成员名称的字符串。

Given enum Suit{Heart, Spade, Club, Diamond}, Suit.Heart.name()will give Heart.

给予enum Suit{Heart, Spade, Club, Diamond}Suit.Heart.name()将给予Heart

Every enum has a valueOf()method, which takes an enum type and a string, to perform the reverse operation:

每个枚举都有一个valueOf()方法,它接受一个枚举类型和一个字符串,来执行相反的操作:

Enum.valueOf(Suit.class, "Heart")returns Suit.Heart.

Enum.valueOf(Suit.class, "Heart")返回Suit.Heart

Why anyone would use ordinals is beyond me. It may be nanoseconds faster, but it is not safe, if the enum members change, as another developer may not be aware some code is relying on ordinal values (especially in the JSP page cited in the question, network and database overhead completely dominates the time, not using an integer over a string).

为什么有人会使用序数是我无法理解的。可能快了纳秒,但不安全,如果枚举成员发生变化,因为另一个开发人员可能没有意识到某些代码依赖于序数值(特别是在问题中引用的 JSP 页面中,网络和数据库开销完全支配了时间,而不是在字符串上使用整数)。

回答by QED

If I'm going to be using values()a lot:

如果我要使用values()很多:

enum Suit {
   Hearts, Diamonds, Spades, Clubs;
   public static final Suit values[] = values();
}

Meanwhile wherever.java:

同时where.java:

Suit suit = Suit.values[ordinal];

Mind your array bounds.

注意你的数组边界。

回答by jmkelm08

I agree with most people that using ordinal is probably a bad idea. I usually solve this problem by giving the enum a private constructor that can take for example a DB value then create a static fromDbValuefunction similar to the one in Jan's answer.

我同意大多数人的看法,即使用序数可能是个坏主意。我通常通过为枚举提供一个私有构造函数来解决这个问题,该构造函数可以采用例如一个 DB 值,然后创建一个fromDbValue类似于 Jan 答案中的静态函数。

public enum ReportTypeEnum {
    R1(1),
    R2(2),
    R3(3),
    R4(4),
    R5(5),
    R6(6),
    R7(7),
    R8(8);

    private static Logger log = LoggerFactory.getLogger(ReportEnumType.class);  
    private static Map<Integer, ReportTypeEnum> lookup;
    private Integer dbValue;

    private ReportTypeEnum(Integer dbValue) {
        this.dbValue = dbValue;
    }


    static {
        try {
            ReportTypeEnum[] vals = ReportTypeEnum.values();
            lookup = new HashMap<Integer, ReportTypeEnum>(vals.length);

            for (ReportTypeEnum  rpt: vals)
                lookup.put(rpt.getDbValue(), rpt);
         }
         catch (Exception e) {
             // Careful, if any exception is thrown out of a static block, the class
             // won't be initialized
             log.error("Unexpected exception initializing " + ReportTypeEnum.class, e);
         }
    }

    public static ReportTypeEnum fromDbValue(Integer dbValue) {
        return lookup.get(dbValue);
    }

    public Integer getDbValue() {
        return this.dbValue;
    }

}

Now you can change the order without changing the lookup and vice versa.

现在您可以在不更改查找的情况下更改顺序,反之亦然。

回答by gerardw

This is what I use. I make no pretense that it's far less "efficient" than the simpler solutions above. What it does do is provide a much clearer exception message than "ArrayIndexOutOfBounds" when an invalid ordinal value is used in the solution above.

这就是我使用的。我并不假装它比上面更简单的解决方案“效率”要低得多。当在上面的解决方案中使用无效的序数值时,它所做的是提供比“ArrayIndexOutOfBounds”更清晰的异常消息。

It utilizes the fact that EnumSet javadoc specifies the iterator returns elements in their natural order. There's an assert if that's not correct.

它利用了 EnumSet javadoc 指定迭代器以自然顺序返回元素的事实。如果那不正确,则有一个断言。

The JUnit4 Test demonstrates how it's used.

JUnit4 测试演示了它是如何使用的。

 /**
 * convert ordinal to Enum
 * @param clzz may not be null
 * @param ordinal
 * @return e with e.ordinal( ) == ordinal
 * @throws IllegalArgumentException if ordinal out of range
 */
public static <E extends Enum<E> > E lookupEnum(Class<E> clzz, int ordinal) {
    EnumSet<E> set = EnumSet.allOf(clzz);
    if (ordinal < set.size()) {
        Iterator<E> iter = set.iterator();
        for (int i = 0; i < ordinal; i++) {
            iter.next();
        }
        E rval = iter.next();
        assert(rval.ordinal() == ordinal);
        return rval;
    }
    throw new IllegalArgumentException("Invalid value " + ordinal + " for " + clzz.getName( ) + ", must be < " + set.size());
}

@Test
public void lookupTest( ) {
    java.util.concurrent.TimeUnit tu = lookupEnum(TimeUnit.class, 3);
    System.out.println(tu);
}

回答by Ar maj

public enum Suit implements java.io.Serializable, Comparable<Suit>{
  spades, hearts, diamonds, clubs;
  private static final Suit [] lookup  = Suit.values();
  public Suit fromOrdinal(int ordinal) {
    if(ordinal< 1 || ordinal> 3) return null;
    return lookup[value-1];
  }
}

the test class

测试类

public class MainTest {
    public static void main(String[] args) {
        Suit d3 = Suit.diamonds;
        Suit d3Test = Suit.fromOrdinal(2);
        if(d3.equals(d3Test)){
            System.out.println("Susses");
        }else System.out.println("Fails");
    }
}

I appreciate that you share with us if you have a more efficient code, My enum is huge and constantly called thousands of times.

如果您有更高效的代码,我很感激您与我们分享,我的枚举非常庞大,并且不断被调用数千次。

回答by Someone Somewhere

This is what I do on Android with Proguard:

这就是我在 Android 上使用 Proguard 所做的:

public enum SomeStatus {
    UNINITIALIZED, STATUS_1, RESERVED_1, STATUS_2, RESERVED_2, STATUS_3;//do not change order

    private static SomeStatus[] values = null;
    public static SomeStatus fromInteger(int i) {
        if(SomeStatus.values == null) {
            SomeStatus.values = SomeStatus.values();
        }
        if (i < 0) return SomeStatus.values[0];
        if (i >= SomeStatus.values.length) return SomeStatus.values[0];
        return SomeStatus.values[i];
    }
}

it's short and I don't need to worry about having an exception in Proguard

它很短,我不需要担心 Proguard 出现异常

回答by joe pelletier

So one way is to doExampleEnum valueOfOrdinal = ExampleEnum.values()[ordinal];which works and its easy, however, as mentioned before, ExampleEnum.values()returns a new cloned array for every call. That can be unnecessarily expensive. We can solve that by caching the array like so ExampleEnum[] values = values(). It is also "dangerous" to allow our cached array to be modified. Someone could write ExampleEnum.values[0] = ExampleEnum.type2;So I would make it private with an accessor method that does not do extra copying.

因此,一种方法是ExampleEnum valueOfOrdinal = ExampleEnum.values()[ordinal];可行且简单,但是,如前所述,ExampleEnum.values()每次调用都会返回一个新的克隆数组。这可能会不必要地昂贵。我们可以通过像这样缓存数组来解决这个问题ExampleEnum[] values = values()。允许修改我们的缓存数组也是“危险的”。有人可以写ExampleEnum.values[0] = ExampleEnum.type2;所以我会使用不进行额外复制的访问器方法将其设为私有。

private enum ExampleEnum{
    type0, type1, type2, type3;
    private static final ExampleEnum[] values = values();
    public static ExampleEnum value(int ord) {
        return values[ord];
    }
}

You would use ExampleEnum.value(ordinal)to get the enum value associated with ordinal

您将用于ExampleEnum.value(ordinal)获取与ordinal