web-dev-qa-db-ja.com

列挙型の序数から列挙型への変換

私はすべてのクラスのメソッド間で渡される列挙型ReportTypeEnumを持っていますが、URLでこれを渡す必要があるので、順序メソッドを使用してint値を取得します。他のJSPページで取得した後、ReportTypeEnumに戻す必要があるので、引き続き渡すことができます。

序数をReportTypeEnumに変換するにはどうすればよいですか?

Java 6 SEを使用します。

281
Lennie

序数を列挙表現に変換するには、次のようにしたい場合があります。

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

配列の境界に注意してください。

values()を呼び出すたびに、新しくクローン化された配列が返されるため、パフォーマンスに悪影響を与える可能性があります。頻繁に呼び出される場合は、配列をキャッシュすることをお勧めします。

values()をキャッシュする方法のコード例


この回答は、コメント内に与えられたフィードバックを含めるために編集されました

577
Joachim Sauer

これはほぼ間違いなく悪い考えです。確かに序数がde-facto永続化されている場合(誰かがURLをブックマークしたなど)-それは、今後enumの順序を常に維持する必要があることを意味します。これは、コードメンテナーにとっては明らかではないかもしれません。

代わりにmyEnumValue.name()を使用してenumをエンコード(およびReportTypeEnum.valueOf(s)を介してデコード)しませんか?

136
oxbow_lakes

values()をたくさん使用する場合:

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

一方、Java:

Suit suit = Suit.values[ordinal];

配列の境界に注意してください。

87
QED

could静的ルックアップテーブルを使用します。

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

私は、序数を使用することはおそらく悪い考えであることをほとんどの人に同意します。通常、この問題を解決するには、enumに、たとえばDB値を取得できるプライベートコンストラクターを指定し、Janの答えに似た静的なfromDbValue関数を作成します。

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

}

これで、ルックアップを変更せずに順序を変更でき、逆も同様です。

7
jmkelm08

これは私が使用するものです。上記の単純なソリューションよりもはるかに「効率が悪い」とは見せかけません。上記のソリューションで無効な序数値が使用されている場合、「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);
}
4
gerardw

これは、Proguardを使用してAndroidで行うことです。

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

それは短く、Proguardで例外が発生することを心配する必要はありません。

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

テストクラス

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

あなたがより効率的なコードを持っているなら、私たちと共有することを感謝します、私のenumは巨大で、常に何千回も呼び出されます。

0
Ar maj

そのため、1つの方法はdoExampleEnum 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];
    }
}

ExampleEnum.value(ordinal)を使用して、ordinalに関連付けられた列挙値を取得します。

0
joe pelletier

次のような簡単なメソッドを定義できます。

public enum Alphabet{
    A,B,C,D;

    public static Alphabet get(int index){
        return Alphabet.values()[index];
    }
}

そして次のように使用します:

System.out.println(Alphabet.get(2));
0
Amir Forsati