int値に関連付けられた列挙型の取得


88

以前は、LegNo列挙型を次のように定義していました。

NO_LEG, LEG_ONE, LEG_TWO

を呼び出すことでreturn LegNo.values()[i];、各列挙型に関連付けられた値を取得できました。

しかし、今ではLegNoenum NO_LEGを0ではなくint -1 にしたいので、プライベートコンストラクターを使用してそのint値を初期化および設定することにしました

NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

private LegNo(final int leg) { legNo = leg; }

唯一のことは、この方法で実行しているため、values()メソッドがNO_LEG列挙型に対して機能しないことです。intに関連付けられた列挙型を取得するにはどうすればよいですか?case switchステートメントまたはif-elseif-elseifを使用する以外に、これを行う効率的な方法はありますか

enumからのint値の取得に関連する多くのSOの質問を見ることができますが、その逆です。

回答:


146

2018年8月の編集

今日、私はこれを次のように実装します

public enum LegNo {
    NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

    private final int value;

    LegNo(int value) {
        this.value = value;
    }

    public static Optional<LegNo> valueOf(int value) {
        return Arrays.stream(values())
            .filter(legNo -> legNo.value == value)
            .findFirst();
    }
}

列挙内でマッピングを維持する必要があります。

public enum LegNo {
    NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

    private int legNo;

    private static Map<Integer, LegNo> map = new HashMap<Integer, LegNo>();

    static {
        for (LegNo legEnum : LegNo.values()) {
            map.put(legEnum.legNo, legEnum);
        }
    }

    private LegNo(final int leg) { legNo = leg; }

    public static LegNo valueOf(int legNo) {
        return map.get(legNo);
    }
}

静的ブロックは1回だけ呼び出されるため、ここでは実質的にパフォーマンスの問題はありません。

編集:valueOf他のJavaクラスとよりインラインになるように、メソッドの名前をに変更しました。


申し訳ありませんが、私が十分に明確であったかどうかはわかりません。私はintを渡し、それに関連付けられた列挙型を取得したいです。
L-Samuels 2012年

@ L-Samuelsはあなたの質問を正しく読んでいないと思います。私のアップデートを見てください。
adarshr 2012年

2
これは明らかなようですが、次のように使用しますLegNo foo = LegNo.valueOf(2);。前のコードはを返しますLegNo.LEG_TWO
FirstOne 2017年

1
返され(マッピングされていない)無効な整数値を渡し、留意すべきでnull使用して予想通り、HashMap.getを戻り指定されたキーがマッピングされた値、またはnullこのマップは、キーのマッピングが含まれていない場合。
FirstOne 2017年

ストリーム構文は適切ですが、静的マップ(確かにメモリ消費量が多い)よりも時間の複雑さが高いことを指摘しておく価値があります。3つの値の問題ではありませんが、valueOf()別のループ内で1000メンバーの列挙型を使用している場合は確実に問題になります。
Patrick M

24

すべてのメンバーを反復処理して正しいものを返す静的メソッドを列挙型に含めることもできます。

public enum LegNo {
   NO_LEG(-1),
   LEG_ONE(1),
   LEG_TWO(2);

   private int legIndex;

   private LegNo(int legIndex) { this.legIndex = legIndex; }

   public static LegNo getLeg(int legIndex) {
      for (LegNo l : LegNo.values()) {
          if (l.legIndex == legIndex) return l;
      }
      throw new IllegalArgumentException("Leg not found. Amputated?");
   }
}

整数でEnum値を取得する場合は、次のように使用します。

int myLegIndex = 1; //expected : LEG_ONE
LegNo myLeg = LegNo.getLeg(myLegIndex);

これは、if else ifステートメントを使用するよりもエレガントだと思います。しかし、enumstoでの検索が多いため、@ adarshrによって提案されたマップ戦略の方が適しています。ユーモアに投票してください。
L-Samuels 2012年

1
私もマップ戦略が大好きです。特に、列挙型に多くの値がある場合、またはこのメカニズムを介して非常に頻繁に検索する必要がある場合。ただし、関連付けられたintによる値の検索が比較的まれな場合や、同じ検索要件を持つさまざまな列挙型が多数ある場合は、マップのオーバーヘッドが節約されるため、よりリソースに優しい方法になると思います。さらに、コードが雑然としていないこともわかります。ただし、自分でマップタイプに確実に切り替えるユースケースがいくつかあります。
マイクアドラー

関連する列挙値を序数で派生させてはなりません。静的マップの使用は、Javaアーキテクトが推奨する方法です。
hfontanez

この例では、legIndexフィールドは序数と一致していますが、任意のint値にすることができます。通常のルックアップは実行されません。また、通常の検索が適切でないと思われる理由を挙げてください。
Mike Adler、

1
「足が見つかりません。切断されましたか?」
Gnagy

17

Java 8に適応したadarshrの回答

import static java.util.Arrays.stream;
import static java.util.stream.Collectors.toMap;

import java.util.Map;

public enum LegNo {
    NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

    private final int legNo;

    private final static Map<Integer, LegNo> map =
            stream(LegNo.values()).collect(toMap(leg -> leg.legNo, leg -> leg));

    private LegNo(final int leg) {
        legNo = leg;
    }

    public static LegNo valueOf(int legNo) {
        return map.get(legNo);
    }
}

11

列挙型LegNoでvalues()メソッドを呼び出すだけで、指定された整数値に対応するEnum値にアクセスすることもできます。LegNo列挙型のフィールドを返します。 LegNo.values()[0]; //returns LEG_NO LegNo.values()[1]; //returns LEG_ONE LegNo.values()[2]; //returns LEG_TWO

正確に彼が探していたものではありませんが、かなり近くにあり、非常に単純です。(対象は死んでいますが、他の誰かにとって役立つかもしれません。)


6

デフォルト値のJava 8ウェイ:

public enum LegNo {
    NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

    private final int legNo;

    LegNo(int legNo) {
        this.legNo = legNo;
    }

    public static LegNo find(int legNo, Supplier<? extends LegNo> byDef) {
        return Arrays.asList(LegNo.values()).stream()
                .filter(e -> e.legNo == legNo).findFirst().orElseGet(byDef);
    }
}

呼び出す:

LegNo res = LegNo.find(0, () -> LegNo.NO_LEG);

または例外付き:

LegNo res = LegNo.find(0, () -> {
    throw new RuntimeException("No found");
});

1

enumには3つの要素しか含まれていないため、if else提案されているように、一連のを使用するのが最も速い方法です。

編集:adarshrが提供した答えは、多くの列挙値が存在する一般的なケースに適していますが、それはあなたの問題には行き過ぎだと思います。


Mapコードにa を含めることは確かに過剰なことではありません。その上、この方法はif-else条件のスパゲッティよりもずっとクリーンになります。
adarshr 2012年

列挙型の値が多数ある場合は、マップの方が優れていることに同意しますが、3つの値の場合は、if / else構成を使用します。好みの問題だと思います。
DieterDP 2012年

どの方法を選択しても、のメソッドシグネチャはpublic LegNo valueOf(int value)変更しないでください。次に、if-elseをenum自体の中に書き込むことができます。if-elseが列挙型から外れた場合、それは確かにそれほどクリーンではないコードになります。
adarshr 2012年

1
私はあなたに完全に同意します:)
DieterDP '15 / 06/15

1
public enum LegNo {
    NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

    private int legNo;

    private LegNo(int leg) { legNo = leg; }

    public static LegNo valueOf(int legNo) {
        for (LegNo leg : LegNo.values()) {
            if (leg.legNo == legNo) return leg;
        }   
    }
}

assert LegNo.valueOf(2) == LegNo.LEG_TWO
assert LegNo.valueOf(3) == null

4
値が10未満の列挙型では受け入れ可能ですが、ルックアップの複雑さO(n)により、多数の列挙型値ではまったく効果がありません
Alfishe

1
public enum LegNo {

  NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

  private final int code;

  LegNo(int code) {
    this.code = code;
    ReverseStorage.reverseMap.put(code, this);
  }

  public static Optional<LegNo> getByCode(int code) {
    return Optional.ofNullable(ReverseStorage.reverseMap.get(code));
  }

  private static final class ReverseStorage {
    private static final Map<Integer, LegNo> reverseMap = new LinkedHashMap<>();
  }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.