从枚举序号转换为枚举类型

Len*_*nie 292 java enums

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

我如何将序数转换为ReportTypeEnum

使用Java 6 SE.

Joa*_*uer 590

要将序数转换为其枚举represantation,您可能希望这样做:

ReportTypeEnum value = ReportTypeEnum.values()[ordinal];
Run Code Online (Sandbox Code Playgroud)

请注意数组边界.

请注意,每次调用都会values()返回一个新克隆的数组,这可能会以负面的方式影响性能.如果要经常调用它,您可能希望缓存该数组.

关于如何缓存的代码示例values().


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


oxb*_*kes 136

这几乎肯定是一个坏主意.当然,如果序数是事实上持久化的(例如因为某人已经为URL添加了书签) - 这意味着您必须始终保留enum将来的顺序,这对于维护人员的编码可能并不明显.

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

  • 如果更改枚举的名称(但保留顺序)怎么办? (21认同)
  • 我同意.在公共API中,更改枚举的名称会破坏向后兼容性,但不会更改顺序.因此,将名称用作"关键"更有意义 (7认同)
  • @Arne - 我认为这比一些没有经验的人出现并且在开头或正确的字母/逻辑位置添加"值"的可能性要小得多.(逻辑上我的意思是例如`TimeUnit`值具有逻辑位置) (6认同)
  • 我当然更喜欢强制枚举顺序而不是枚举的名称...这就是为什么我更喜欢在数据库中存储序号而不是枚举的名称.此外,最好使用int操作而不是String ... (6认同)
  • 更好的主意 (3认同)
  • 对于值的生存时间比应用程序更长的情况,这是一个很好的警告。通过将枚举的序数写入文件或数据库,您(可能在不知不觉中)假设枚举值的顺序永远不会改变。但在很多情况下,枚举的值只会在程序执行期间使用。因此,请明智地选择并考虑约阿希姆·绍尔的答复。它实际上回答了手头的问题。;) (2认同)
  • 存储序数可以更轻松地将您的想法翻译成其他语言.如果你需要在C中编写一些组件怎么办? (2认同)
  • 让我们不要从一个措辞简洁的问题中猜测OP的持久性要求.也许该值不会保留在有线格式中的时间超过单个用户会话.或许它确实如此. (2认同)

QED*_*QED 89

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

enum Suit {
   Hearts, Diamonds, Spades, Clubs;
   public static final Suit values[] = values();
}
Run Code Online (Sandbox Code Playgroud)

同时wherever.java:

Suit suit = Suit.values[ordinal];
Run Code Online (Sandbox Code Playgroud)

注意阵列界限.

  • 这非常令人担忧,因为**数组是可变的**.即使values []是最终的,它也不会阻止`Suit.values [0] = Suit.Diamonds;`代码中的某处.理想情况下,这种情况永远不会发生,但*不暴露可变字段*的整体原则仍然存在.对于这种方法,请考虑使用`Collections.unmodifiableList`等. (8认同)
  • +1这是迄今为止最好的解决方案恕我直言,因为一个人可以传递序数,特别是在android.os.Message. (3认同)
  • @Pratyush可以使数组变量不可变,但不可变。我仍然可以做getValues()[0] = somethingElse; (3认同)

jmk*_*m08 9

我同意大多数人使用序数可能是一个坏主意.我通常通过给枚举一个私有构造函数来解决这个问题,该构造函数可以采用例如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;
    }

}
Run Code Online (Sandbox Code Playgroud)

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


Jan*_*Jan 7

可以使用静态查找表:

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);
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 哇!哇哇!当然,这很简洁,但是......你知道 - 我内心的C程序员痛苦地看到你分配了一个完整的HashMap并在其中执行查找所有它只是基本上管理4个常量:黑桃,心脏,钻石和俱乐部!AC程序员将为每个分配1个字节:'const char CLUBS = 0;' 等等...是的,HashMap查找是O(1),但是HashMap的内存和CPU开销,在这种情况下比直接调用.values()要慢许多个数量级和资源饥饿!难怪如果人们这样写,Java就是如此的记忆力...... (10认同)
  • 另请参阅[Enums](http://download.oracle.com/javase/1.5.0/docs/guide/language/enums.html). (3认同)
  • 并非每个程序都需要执行三A游戏.在许多情况下,交易内存和CPU的类型安全性,可读性,可维护性,跨平台支持,垃圾收集等......是合理的.出于某种原因存在更高级别的语言. (2认同)
  • 但是如果你的键范围总是'0 ...(n-1)`,那么数组代码更少,更易读; 性能提升只是一个奖励.`private static final Suit [] VALUES = values();`和`public Suit fromOrdinal(int ordinal){return VALUES [ordinal]; }`.额外优势:立即在无效序数上崩溃,而不是静默返回null.(并不总是有利.但经常.) (2认同)

ger*_*rdw 5

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

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

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);
}
Run Code Online (Sandbox Code Playgroud)