IntをJavaの列挙型にキャストする


331

次の列挙型を与えられたJavaの列挙型にIntをキャストする正しい方法は何ですか?

public enum MyEnum
{
    EnumValue1,
    EnumValue2
}


MyEnum enumValue = (MyEnum) x; //Doesn't work???

これはあなたの質問に答えますか?int値に関連付けられた列挙型の取得
Ryu S.

@RyuS。それはこの質問の複製ではありません
Mark Rotteveel

回答:


583

MyEnum.values()[x]where x0or でなければならない1、つまり、その列挙型の有効な序数を試してください。

Javaで実際のクラス(と列挙値はこのようにオブジェクトである)されている列挙型ことに注意してくださいので、あなたはキャストすることはできませんintかさえInteger列挙型に。


115
+1:キャッシュMyEnum.values()が高価になる可能性があります。つまり、何百回と呼ばれる場合です。
Peter Lawrey、

6
@PeterLawrey完全を期すために、なぜそれが遅いのかを説明できますか?その理由は明らかではない。
Tarrasch

48
@Tarrasch配列は変更可能なため、万が一変更した場合に備えて、values()は要素の配列のコピーを返す必要があります。毎回このコピーを作成すると、比較的コストがかかります。
Peter Lawrey、

9
@PeterLawrey私は最近(すべてが不変である)多くのhaskellに使用しています!明確で簡潔な説明をありがとうございます。:)
Tarrasch

「x」を取得する方法?
Beking Jk

160

MyEnum.values()[x]高価な操作です。パフォーマンスが気になる場合は、次のようにすることをお勧めします。

public enum MyEnum {
    EnumValue1,
    EnumValue2;

    public static MyEnum fromInteger(int x) {
        switch(x) {
        case 0:
            return EnumValue1;
        case 1:
            return EnumValue2;
        }
        return null;
    }
}

44
スイッチのメンテナンスを避けたい場合は、次のクラスを使用します。private final MyEnum [] myEnumValues = MyEnum.values(); 次に、使用法:myEnum = myEnumValues [i];
Gili Nachum

23
@GiliNachumは奇妙な言い方をしましたが、このソリューションの問題は保守性です。これはDRYの原則に反します。つまり、列挙型の値が変更されるたびに(並べ替え、値の追加、値の削除)、switchステートメントは同時に更新する必要があります。Giliのコメントにより、Javaは列挙値のリストとの一貫性を維持する必要があり、列挙値の変更はそのアプローチにまったく影響しません。
Dandre Allison

3
@LorenzoPolidori、あなたがなぜあなたがMyEnum.values()[x]高価な操作と見なすのか説明できますか?詳細はわかりませんが、配列内の要素にアクセスすることは、大したことではなく、一定の時間であるように思えます。アレイを構築する必要がある場合、O(n)時間かかります。これは、ソリューションと同じ実行時間です。
brunsgaard 2013年

1
@brunsgaard values()配列は可変なので、同じ配列を複数回返すのは安全ではないため、毎回新しい配列を生成すると思います。switchステートメントは必ずしもO(n)である必要はありません。それらはジャンプテーブルにコンパイルできます。したがって、ロレンツォの主張は正当化されているようです。
MikeFHay 2014年

1
@Johnson Giliのバージョンでは、Enum宣言の変更以外の追加のコード変更は必要ありません。列挙型に新しい項目を追加した場合myEnum = myEnumValues[i]でも、i変更なしで列挙型のth番目の項目が返されます。
Dandre Allison

45

整数値を指定する場合は、以下のような構造を使用できます

public enum A
{
        B(0),
        C(10),
        None(11);
        int id;
        private A(int i){id = i;}

        public int GetID(){return id;}
        public boolean IsEmpty(){return this.equals(A.None);}
        public boolean Compare(int i){return id == i;}
        public static A GetValue(int _id)
        {
            A[] As = A.values();
            for(int i = 0; i < As.length; i++)
            {
                if(As[i].Compare(_id))
                    return As[i];
            }
            return A.None;
        }
}

5
+1は、値が連続している必要がないという事実を強調しているためです。
rhavin 2013

また、これは、0 ..(count-1)のデフォルトの序数を使用するのではなく、スパースな(または繰り返しの)整数値のセットが必要な場合に機能する唯一の答えのようです。ネットワークを介するなど、既存のコードとやり取りする場合は、これが重要になることがあります。
benkc 2013

上記の回答のように、values()の結果をキャッシュすることは、呼び出すたびにメモリ割り当てとarraycopyを回避するために、おそらく価値があることを指摘する価値があります。
benkc 2013

1
また、列挙の長さに応じて、毎回線形検索を実行するのではなく、HashMapを作成するか、この検索にバイナリ検索などを使用することをお勧めします。
benkc 2013

2
これはintをenumに変換する正しい方法とベストプラクティスである必要があります。パブリック静的A GetValue(int _id){for(A a:A.values(){if(a.getId( )== _ id){return a;}} return null;} None、isEmpty()、compare()などを取り除く
Chris.Zou

35

このようにしてみてください。
要素IDを持つクラスを作成します。

      public Enum MyEnum {
        THIS(5),
        THAT(16),
        THE_OTHER(35);

        private int id; // Could be other data type besides int
        private MyEnum(int id) {
            this.id = id;
        }

        public static MyEnum fromId(int id) {
                for (MyEnum type : values()) {
                    if (type.getId() == id) {
                        return type;
                    }
                }
                return null;
            }
      }

次に、idをintとして使用して、このEnumをフェッチします。

MyEnum myEnum = MyEnum.fromId(5);

19

値をキャッシュして、単純な静的アクセスメソッドを作成します。

public static enum EnumAttributeType {
    ENUM_1,
    ENUM_2;
    private static EnumAttributeType[] values = null;
    public static EnumAttributeType fromInt(int i) {
        if(EnumAttributeType.values == null) {
            EnumAttributeType.values = EnumAttributeType.values();
        }
        return EnumAttributeType.values[i];
    }
}

4
これは私が現在使用しているソリューションです。しかし、私見では、フィールドvaluesにメソッドと同じ名前を付けない方が混乱が少なくなりますvalues()cachedValuesフィールド名に使用しています。
ToolmakerSteve 2015年

2
効率的でエレガント。私のプロジェクトにコピーして貼り付けました:)変更したのはfromInt(int i)だけです。これは、署名にintを2回含めるのは少し冗長なので、from(int i)と呼んでいます。
pipedreambomb

1
最初から初期化しないのはなぜですか?なぜ最初のヒットを待つのですか?
カイザー2018

9

Java列挙型には、C ++で行うのと同じ種類の列挙型から整数へのマッピングがありません。

つまり、すべての列挙型には、values可能な列挙型値の配列を返すメソッドがあるため、

MyEnum enumValue = MyEnum.values()[x];

うまくいくはずです。これは少し厄介で、可能であればintsからEnums(またはその逆)に変換しない方がよいでしょう。


9

これは通常行われることではないので、再考します。ただし、基本的な操作は次のとおりです。int-> EnumType.values()[intNum]を使用した列挙型、およびenumInst.ordinal()を使用した列挙型-> int。

ただし、values()の実装には配列のコピーを提供するしかありません(Java配列は読み取り専用ではありません)ため、EnumMapを使用してenum-> intマッピングをキャッシュするほうが適切です。


2
「これは通常行われることではありません」について:有用な一般的なケース:enumはデータベースに格納されているint値に対応します。
ToolmakerSteve 2015年

@ToolmakerSteveマッピングが必要であることは完全に正しいです。しかし、そのようなエンコーディングをORマッパーまたはツールキット/ライブラリに任せたいですか?
Dilum Ranatunga 2015


6

これが私が計画している解決策です。これは非順次整数で機能するだけでなく、列挙値の基礎となるIDとして使用する可能性のある他のデータ型でも機能するはずです。

public Enum MyEnum {
    THIS(5),
    THAT(16),
    THE_OTHER(35);

    private int id; // Could be other data type besides int
    private MyEnum(int id) {
        this.id = id;
    }

    public int getId() {
        return this.id;
    }

    public static Map<Integer, MyEnum> buildMap() {
        Map<Integer, MyEnum> map = new HashMap<Integer, MyEnum>();
        MyEnum[] values = MyEnum.values();
        for (MyEnum value : values) {
            map.put(value.getId(), value);
        }

        return map;
    }
}

特定の時間(ファイルからデータをロードするとき)にIDを列挙型に変換するだけでよいので、マップを常にメモリに保持する理由はありません。いつでもマップにアクセスできるようにする必要がある場合は、いつでもEnumクラスの静的メンバーとしてマップをキャッシュできます。


私見メモリ使用量が気になる場合は、@ ossysのように動的にHashMapを作成しますが、キャッシュがnullの場合は別のコードclearCachedValuesを使用し、使用が終わったら2番目のメソッドを追加します(プライベートフィールドをnullに戻します)。MyEnum.fromInt(i)マップオブジェクトを渡すよりも理解しやすいと思います。
ToolmakerSteve 2015年

6

他の人を助けるために、ここにリストされていない私が好むオプションは、グアバのマップ機能を使用しています

public enum MyEnum {
    OPTION_1(-66),
    OPTION_2(32);

    private int value;
    private MyEnum(final int value) {
        this.value = value;
    }

    public int getValue() {
        return this.value;
    }

    private static ImmutableMap<Integer, MyEnum> reverseLookup = 
            Maps.uniqueIndex(Arrays.asList(MyEnum.values())), MyEnum::getValue);

    public static MyEnum fromInt(final int id) {
        return reverseLookup.getOrDefault(id, OPTION_1);
    }
}

デフォルトではを使用nullできthrow IllegalArgumentExceptionますfromIntOptional、好きな動作をすることができます。


3
Guavaを使用していることを説明する必要があります。または、ストリームを使用することもできますMap<Integer, MyEnum> reverseLookup = Arrays.stream(MyEnum.values()).collect(Collectors.toMap(MyEnum::getValue, Function.identity()));
。– shmosel

1
getValue()メソッドも定義したいかもしれません。
shmosel

@shmoselおっと、おかげでgetValue関数を逃しました。ジェネリックバージョンをスタックオーバーフローに入力しても、常にうまくいくとは限りません。リンク付きのGuavaの使用に関するコメントを追加しました。ストリームよりもグアバメソッドを優先します。
Chad Befus 2017年

3

values()enumを反復処理して、enumの整数値をid以下のように指定された値と比較できます。

public enum  TestEnum {
    None(0),
    Value1(1),
    Value2(2),
    Value3(3),
    Value4(4),
    Value5(5);

    private final int value;
    private TestEnum(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }

    public static TestEnum  getEnum(int value){
        for (TestEnum e:TestEnum.values()) {
            if(e.getValue() == value)
                return e;
        }
        return TestEnum.None;//For values out of enum scope
    }
}

そして、このように使用してください:
TestEnum x = TestEnum.getEnum(4);//Will return TestEnum.Value4
これが役に立てば幸いです;)


3

@ChadBefusの回答と@shmoselコメントに基づいて、これを使用することをお勧めします。(効率的なルックアップ、および純粋なJava> = 8で動作)

import java.util.stream.Collectors;
import java.util.function.Function;
import java.util.Map;
import java.util.Arrays;

public enum MyEnum {
    OPTION_1(-66),
    OPTION_2(32);

    private int value;
    private MyEnum(final int value) {
        this.value = value;
    }

    public int getValue() {
        return this.value;
    }

    private static Map<Integer, MyEnum> reverseLookup =
        Arrays.stream(MyEnum.values()).collect(Collectors.toMap(MyEnum::getValue, Function.identity()));

    public static MyEnum fromInt(final int id) {
        return reverseLookup.getOrDefault(id, OPTION_1);
    }
    public static void main(String[] args) {
        System.out.println(fromInt(-66).toString());
    }
}

0

良いオプションがあるに避けてから変換intするenum:あなたは最大値が必要な場合たとえば、あなたは(y.ordinal)、それに応じてリターンxまたはyに)(x.ordinal比較することができます。(このような比較を意味のあるものにするために、値を並べ替える必要があるかもしれません。)

それが不可能な場合はMyEnum.values()、静的配列に格納します。


1
あなたがDBからint型を取得し、私は非常に一般的なタスクがあると思い列挙型、に変換したい場合は、int型をする必要があります- >列挙型の変換を、IMO ..
ゆき井上

0

これは医師と同じ答えですが、可変配列の問題を解消する方法を示しています。最初に分岐予測のためにこの種のアプローチを使用する場合、ifの効果はほとんどなく、コード全体は可変配列のvalues()関数を1回だけ呼び出します。どちらの変数も静的であるため、この列挙の使用ごとにn *メモリを消費しません。

private static boolean arrayCreated = false;
private static RFMsgType[] ArrayOfValues;

public static RFMsgType GetMsgTypeFromValue(int MessageID) {
    if (arrayCreated == false) {
        ArrayOfValues = RFMsgType.values();
    }

    for (int i = 0; i < ArrayOfValues.length; i++) {
        if (ArrayOfValues[i].MessageIDValue == MessageID) {
            return ArrayOfValues[i];
        }
    }
    return RFMsgType.UNKNOWN;
}

0
enum MyEnum {
    A(0),
    B(1);
    private final int value;
    private MyEnum(int val) {this.value = value;}
    private static final MyEnum[] values = MyEnum.values();//cache for optimization
    public static final getMyEnum(int value) { 
        try {
            return values[value];//OOB might get triggered
        } catch (ArrayOutOfBoundsException e) {
        } finally {
            return myDefaultEnumValue;
        }
    }
}


@shmosel範囲外が比較的非常に少ない場合、例外は大幅に改善されると思います。
ゾソ2017年

あなたの信念は何に基づいていますか?
shmosel

0

コトリンでは:

enum class Status(val id: Int) {
    NEW(0), VISIT(1), IN_WORK(2), FINISHED(3), CANCELLED(4), DUMMY(5);

    companion object {
        private val statuses = Status.values().associateBy(Status::id)

        fun getStatus(id: Int): Status? = statuses[id]
    }
}

使用法:

val status = Status.getStatus(1)!!

0

この実装を書きました。欠損値、負の値を許容し、コードの一貫性を保ちます。マップもキャッシュされます。インターフェイスを使用し、Java 8が必要です。

列挙型

public enum Command implements OrdinalEnum{
    PRINT_FOO(-7),
    PRINT_BAR(6),
    PRINT_BAZ(4);

    private int val;
    private Command(int val){
        this.val = val;
    }

    public int getVal(){
        return val;
    }

    private static Map<Integer, Command> map = OrdinalEnum.getValues(Command.class);
    public static Command from(int i){
        return map.get(i);
    }
}

インターフェース

public interface OrdinalEnum{
    public int getVal();

    @SuppressWarnings("unchecked")
    static <E extends Enum<E>> Map<Integer, E> getValues(Class<E> clzz){
        Map<Integer, E> m = new HashMap<>();
        for(Enum<E> e : EnumSet.allOf(clzz))
            m.put(((OrdinalEnum)e).getVal(), (E)e);

        return m;
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.