列挙型序数から列挙型に変換


316

ReportTypeEnumすべてのクラスのメソッド間で渡される列挙型がありますが、次にこれをURLに渡して、序数メソッドを使用してint値を取得する必要があります。他のJSPページで取得した後、ReportTypeEnum引き続きそれを渡すことができるように、それをに変換する必要があります。

序数をどのように変換できReportTypeEnumますか?

Java 6 SEを使用します。


1
これまでJava 6 EEはありませんでした(AFAIK)。5. Java SEの6、およびJava EEあり
Hosamアリー

回答:


632

序数を列挙型に変換するには、次のようにします。

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

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

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

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


この回答は、コメント内のフィードバックを含むように編集されました


このソリューションを実装しましたが、うまくいきません。序数値は、列挙型が追加された順序と一致するとは限りません。それがこの答えが主張していることは私にはわかりませんが、それでも人々に警告したかったです
IcedD​​ante

@IcesDante:序数は、ソース内の列挙値の順序に対応することが保証されています。別の行動を観察する場合、何か他のことが間違っている必要があります。ただし、上記の私の回答は、他の回答で説明されているすべての理由から最適ではありません。
Joachim Sauer

@JoachimSauer Maybe IcedD​​anteは、序数が以前のバージョンのソースによって生成および保存された場合、列挙値が異なる順序であったため、序数が一致しない可能性があることを意味します。
LarsH

137

これはほぼ間違いなく悪い考えです。確かに、序数が事実上存続している場合(たとえば、誰かがURLをブックマークしたため)-これはenum、将来、常に順序を保持する必要があることを意味します。

代わりにenum使用してエンコードmyEnumValue.name()(そしてを介してデコードReportTypeEnum.valueOf(s))しないのはなぜですか?


24
列挙型の名前を変更するとどうなりますか?
Arne Evertsson、2009

6
@Arne-これは、経験の浅い人が一緒に来てvalue、最初またはその正しいアルファベット/論理的な位置にを追加するよりもはるかに可能性が低いと思います。(論理的には、たとえばTimeUnit値に論理的な位置があることを意味します)
oxbow_lakes '11

7
私は、列挙型の名前ではなく列挙型の順序を強制することを確実に好みます。これが、列挙型の名前ではなく序数をデータベースに格納することを好む理由です。さらに、Stringではなくint操作を使用する方がよい...

8
同意する。パブリックAPIでは、Enumの名前を変更すると下位互換性が失われますが、順序を変更しても変わりません。そのため、名前を「キー」として使用する方が理にかなっています
Noel

3
序数を保存すると、アイデアを他の言語に簡単に翻訳できます。Cでコンポーネントを作成する必要がある場合
QED 2015年

94

私がvalues()たくさん使うつもりなら:

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

一方wherewhere.java:

Suit suit = Suit.values[ordinal];

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


3
+1これは、特にandroid.os.Messageで序数を渡すことができるため、これは断然最良の解決策です。
likejudo 2017年

9
配列は変更可能であるため、これは非常に心配です。values []は最終的なものですがSuit.values[0] = Suit.Diamonds;、コードのどこかを妨げるものではありません。理想的にはそれは決して起こらないでしょうが、変更可能なフィールドを公開しないという全体的な原則はまだ成り立ちます。このアプローチでは、Collections.unmodifiableList代わりになどを使用することを検討してください。
Mshnik 2017年

方法について-プライベートの静的な最終スート値[] = values(); public static Suit [] getValues(){戻り値; }
Pratyush

4
@Pratyushは配列変数を不変にしますが、その内容は変更しません。まだgetValues()[0] = somethingElse;を実行できました。
カラバシン

同意し、したがって、ポイントはSuit values[]直接的または間接的に公開されません(どのようにを通じて言及されたかgetValues())。ordinal値をどのように引数に送信し、Suitから表現を返す必要があるパブリックメソッドで回避しSuit values[]ます。ここでのポイント(最初からの質問のタイル)は、列挙型序数から列挙型を作成することでした
マヌエルジョーダン

13

序数を使用することはおそらく悪い考えであることを私はほとんどの人に同意します。通常、この問題を解決するには、列挙型に、たとえば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;
    }

}

これで、ルックアップを変更せずに順序を変更でき、その逆も可能です。


これが正解です。他のより直接的だが潜在的に無効な回答(将来のコード変更のため)と比較して、ポイントが非常に少ないことに驚いています。
カラバシン

8

あなたは可能性があり、静的なルックアップテーブルを使用します。

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

3
Enumsも参照してください。
ゴミ神

11
うわー!すごい!もちろん、これはきちんとしていますが...私の内部のCプログラマーは、本格的なHashMapを割り当て、そのすべての中でルックアップを実行して、基本的に4つの定数(スペード、ハート、ダイヤモンドとクラブ!Cプログラマーはそれぞれに1バイトを割り当てます: 'const char CLUBS = 0;' など...はい、HashMapルックアップはO(1)ですが、HashMapのメモリとCPUオーバーヘッドにより、この場合、.values()を直接呼び出すよりも桁違いに遅くなり、リソースを大量に消費します。不思議ではない人はこのように書いた場合、Javaが...、このようなメモリ食いである
レシェク

2
すべてのプログラムがトリプルAゲームのパフォーマンスを必要とするわけではありません。多くの場合、タイプセーフ、可読性、保守性、クロスプラットフォームサポート、ガベージコレクションなどのためにメモリとCPUを交換することは正当化できます。より高いレベルの言語が存在するのには理由があります。

3
ただし、キー範囲が常にの0...(n-1)場合、配列はコードが少なく、読みやすくなります。パフォーマンスの向上は単なるボーナスです。private static final Suit[] VALUES = values();public Suit fromOrdinal(int ordinal) { return VALUES[ordinal]; }。追加の利点:暗黙的にnullを返すのではなく、無効な序数ですぐにクラッシュします。(常に利点があるとは限りません。しかし、多くの場合。)
トーマス

4

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

1

これは私が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];
    }
}

それは短く、私はプロガードで例外があることを心配する必要はありません


1

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

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

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

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

System.out.println(Alphabet.get(2));

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

より効率的なコードがある場合は、私たちと共有していただきありがとうございます。私の列挙型は巨大で、常に何千回も呼び出されます。


「if(ordinal <1 || ordinal> 4)return null;」の意味だと思います。
geowar

0

したがって、1つの方法は、どちら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];
    }
}

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


-1

すべてのenumにはname()があり、enumメンバーの名前の文字列を提供します。

与えられenum Suit{Heart, Spade, Club, Diamond}Suit.Heart.name()与えHeartます。

すべての列挙valueOf()型には、列挙型と文字列を取り、逆の操作を実行するメソッドがあります。

Enum.valueOf(Suit.class, "Heart")を返しますSuit.Heart

なぜ誰もが序数を使用する理由は私を超えています。別の開発者が一部のコードが序数値に依存していることに気付かない可能性があるため(特に質問で引用されているJSPページでは、ネットワークとデータベースのオーバーヘッドが完全に支配しているため)時間、文字列に整数を使用しない)。


2
整数の比較は文字列の比較よりもはるかに速いので?
HighCommander4 2013

2
しかし、誰かが列挙型を変更すると、序数は変更されます(メンバーの追加/並べ替え)。特に、ネットワークレイテンシが整数の配列(文字列)と単一の整数の比較の差が1,000,000倍であるJSPページでは、速度が問題になる場合があります。
Tony BenBrahim 2013

toStringはオーバーライドできるため、列挙名を返さない場合があります。メソッドname()は列挙型の名前を与えるものです(これが最終です)
gerardw

コードのコメントとアプリケーションのバージョンも重要です(たぶん、このようにするとファイル形式がシンプルになり、サイズが小さくなります)
ジョーペレティエ

oxbow_lakesの回答と同じことを本質的に推奨しているのに、なぜこれが反対票だったのかわかりません。序数を使用するよりも間違いなく安全です。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.