整数値を一致するJava列挙型に変換します


86

私はこのような列挙型を持っています:

public enum PcapLinkType {
  DLT_NULL(0)
  DLT_EN10MB(1)
  DLT_EN3MB(2),
  DLT_AX25(3),
  /*snip, 200 more enums, not always consecutive.*/
  DLT_UNKNOWN(-1);
    private final int value;   

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

ここで、外部入力からintを取得し、一致する入力が必要です。値が存在しない場合に例外をスローしても問題ありませんが、その場合は例外をスローすることをお勧めしDLT_UNKNOWN ます。

int val = in.readInt();
PcapLinkType type = ???; /*convert val to a PcapLinkType */

回答:


105

整数を列挙型にマップするクラスに静的マップを追加して、これを手動で行う必要があります。

private static final Map<Integer, PcapLinkType> intToTypeMap = new HashMap<Integer, PcapLinkType>();
static {
    for (PcapLinkType type : PcapLinkType.values()) {
        intToTypeMap.put(type.value, type);
    }
}

public static PcapLinkType fromInt(int i) {
    PcapLinkType type = intToTypeMap.get(Integer.valueOf(i));
    if (type == null) 
        return PcapLinkType.DLT_UNKNOWN;
    return type;
}

1
dtyからの推奨事項で更新されました。これは良い考えでした。
MeBigFatGuy 2011年

私のコードを最初にコンパイラーで実行したことを願っています...私は頭のてっぺんからそれを作り上げました。私はテクニックがうまくいくことを知っています-私は昨日それを使いました。しかし、コードは別のマシンにあり、このマシンには私の開発ツールがありません。
dty 2011年

1
allOfはセットでのみ利用可能
MeBigFatGuy 2011年

1
また、EnumMap列挙型をキーとして使用します。この場合、OPは列挙型を値として必要とします。
dty 2011年

8
これは、多くの不要なオーバーヘッドのようです。このタイプの操作を実際に必要とする人は、ストリーム/ソケットからの書き込み/読み取りを行っているため、おそらく高性能が必要です。その場合、values()(列挙値がシーケンシャルである場合)のキャッシュまたは単純なswitchステートメントはこの方法を簡単に打ち負かします。 。エントリがほんの一握りしかない場合はEnumswitchステートメントを更新する必要がないという便宜のために、HashMapのオーバーヘッドを追加することはあまり意味がありません。この方法はよりエレガントに見えるかもしれませんが、無駄でもあります。
ときめき

30

文書化されている静的メソッドvalues()ありますが、期待する場所にはありません:http//docs.oracle.com/javase/tutorial/java/javaOO/enum.html

enum MyEnum {
    FIRST, SECOND, THIRD;
    private static MyEnum[] allValues = values();
    public static MyEnum fromOrdinal(int n) {return allValues[n];}
}

原則として、だけを使用できますがvalues()[i]values()呼び出されるたびに配列のコピーを作成するという噂があります。


9
Joshua Bloch (Effective Java Book)によると:列挙型に関連付けられた値をその序数から導出しないでください。実装は列挙型の順序に依存するべきではありません。
stevo.mit 2013

4
何の実装何らかのアルゴリズムを実装する場合、その順序が文書化されていない限り、実装は列挙型の順序に依存するべきではありません。列挙型自体を実装する場合、クラスプライベートメソッドを使用するのと同じ方法で、そのような実装の詳細を使用するのは問題ありません。
18446744073709551615 2013

1
同意しないでください。ドキュメントに関係なく、決して意味がないと思います。自分で列挙型を実装する場合でも、序数を使用しないでください。それは悪臭であり、エラーが発生しやすいです。私は専門家ではありませんが、ジョシュア・ブロックとは議論しません:)
stevo.mit 2013

4
@ stevo.mitは、Java 8の新しい列挙型java.time.Monthを確認しています。Month.of(int)静的メソッドは、JoshuaBlochが「絶対に」すべきではないと言ったことを正確に実行します。序数に基づいて月を返します。
Klitos Kyriacou 2015

1
@ stevo.mit順序付き列挙型順序なし列挙型があります。(そしてビットマスク列挙型も。)それらを単なる「列挙型」と呼ぶのは単に間違っています。表現の意味を使用するかどうかの決定は、作業する抽象化レベルに基づいている必要があります。実装の詳細(下位レベルからの表現手段)または使用の仮定(上位レベルからの表現手段)を使用することは確かに正しくありません。「決して」に関しては、人間の言語では決して決して決して意味しません。なぜなら、常に何らかの文脈があるからです。(通常、アプリケーションプログラミングでは、決して...)ところで、programering.com
a

14

PcapLinkType.values()を繰り返して比較する、新しい静的メソッドを作成する必要があります。

public static PcapLinkType forCode(int code) {
    for (PcapLinkType typе : PcapLinkType.values()) {
        if (type.getValue() == code) {
            return type;
        }
    }
    return null;
 }

それがめったに呼び出されない場合、それは問題ありません。頻繁に呼び出される場合はMap、他の人が提案した最適化を見てください。


4
たくさん呼ばれると高額になるかもしれません。静的マップを作成すると、償却コストが向上する可能性があります。
dty 2011年

@dty o(n)with n = 200-私はそれが問題だとは思わない– Bozho 2011
3

7
それは、それがどれほど頻繁に呼び出されるかを感じさせない、まったくばかげた発言です。一度呼ばれたら大丈夫です。10Geネットワーク上でパケットが通過するたびに呼び出される場合は、アルゴリズムを200倍高速化することが非常に重要です。したがって、なぜ私は自分の声明を「たくさん呼ばれたら」で修飾したのか
dty 2011年

10

このようなことを行うと、それらすべてをコレクションに自動的に登録して、整数を対応する列挙型に簡単に変換できます。(ところで、列挙型コンストラクターのマップにそれらを追加することは許可されていません。Javaを長年使用した後でも、新しいことを学ぶのは素晴らしいことです。:)

public enum PcapLinkType {
    DLT_NULL(0),
    DLT_EN10MB(1),
    DLT_EN3MB(2),
    DLT_AX25(3),
    /*snip, 200 more enums, not always consecutive.*/
    DLT_UNKNOWN(-1);

    private static final Map<Integer, PcapLinkType> typesByValue = new HashMap<Integer, PcapLinkType>();

    static {
        for (PcapLinkType type : PcapLinkType.values()) {
            typesByValue.put(type.value, type);
        }
    }

    private final int value;

    private PcapLinkType(int value) {
        this.value = value;
    }

    public static PcapLinkType forValue(int value) {
        return typesByValue.get(value);
    }
}

1
それはあなたが投稿する前にあなたの答えを再確認するためにあなたが得るものです。;)
Esko Luontola 2011年

10

このような列挙型がある場合

public enum PcapLinkType {
  DLT_NULL(0)
  DLT_EN10MB(1)
  DLT_EN3MB(2),
  DLT_AX25(3),
  DLT_UNKNOWN(-1);

    private final int value;   

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

その後、あなたはそれを次のように使用することができます

PcapLinkType type = PcapLinkType.values()[1]; /*convert val to a PcapLinkType */

コメントを見逃しました/ * snip、200以上の列挙型、常に連続しているとは限りません。* /
MeBigFatGuy 2015

列挙値がゼロからの推移性である場合に備えて、これは悪い習慣です
cuasodayleo 2017

4

@MeBigFatGuyが言うように、static {...}ブロックにvalues()コレクションのループを使用させることができることを除いて:

static {
    for (PcapLinkType type : PcapLinkType.values()) {
        intToTypeMap.put(type.getValue(), type);
    }
}

4

私はこの質問が数年前のものであることを知っていますが、Java 8がその間に私たちをもたらしたOptionalので、私はそれを使用して解決策を提供すると思いました(StreamそしてCollectors):

public enum PcapLinkType {
  DLT_NULL(0),
  DLT_EN3MB(2),
  DLT_AX25(3),
  /*snip, 200 more enums, not always consecutive.*/
  // DLT_UNKNOWN(-1); // <--- NO LONGER NEEDED

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

  private static final Map<Integer, PcapLinkType> map;
  static {
    map = Arrays.stream(values())
        .collect(Collectors.toMap(e -> e.value, e -> e));
  }

  public static Optional<PcapLinkType> fromInt(int value) {
    return Optional.ofNullable(map.get(value));
  }
}

Optionalのようなものnullです:(有効な)値がない場合を表します。ただし、またはケースのチェックを忘れる可能性があるnullなどのDLT_UNKNOWN理由で、またはデフォルト値のよりタイプセーフな代替手段です。どちらも有効な値です。対照的に、タイプの変数に値を割り当てることはできません。最初に有効な値を確認します。nullDLT_UNKNOWNPcapLinkTypeOptional<PcapLinkType>PcapLinkTypeOptional

もちろん、DLT_UNKNOWN下位互換性やその他の理由で保持したいOptional場合は、その場合でも、を使用orElse()してデフォルト値として指定することができます。

public enum PcapLinkType {
  DLT_NULL(0),
  DLT_EN3MB(2),
  DLT_AX25(3),
  /*snip, 200 more enums, not always consecutive.*/
  DLT_UNKNOWN(-1);

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

  private static final Map<Integer, PcapLinkType> map;
  static {
    map = Arrays.stream(values())
        .collect(Collectors.toMap(e -> e.value, e -> e));
  }

  public static PcapLinkType fromInt(int value) {
    return Optional.ofNullable(map.get(value)).orElse(DLT_UNKNOWN);
  }
}

3

intパラメータとしてを受け入れてを返す静的メソッドを列挙型に追加できますPcapLinkType

public static PcapLinkType of(int linkType) {

    switch (linkType) {
        case -1: return DLT_UNKNOWN
        case 0: return DLT_NULL;

        //ETC....

        default: return null;

    }
}

switch新しい列挙型を追加する場合は、そのステートメントにエントリを追加することを忘れないでください。理想的ではない、私見。
dty 2011年

1
@dtyでは、HashMapのオーバーヘッドが、switchステートメントに新しいケースを追加する必要性を上回っていると思いますか?
2013

1
ハッシュルックアップのマイクロパフォーマンスに焦点を当てる前に、間違いを犯さないようにするため、正しい可能性が高いコードを作成したいと思います。
dty 2013

3

これは私が使用するものです:

public enum Quality {ENOUGH,BETTER,BEST;
                     private static final int amount = EnumSet.allOf(Quality.class).size();
                     private static Quality[] val = new Quality[amount];
                     static{ for(Quality q:EnumSet.allOf(Quality.class)){ val[q.ordinal()]=q; } }
                     public static Quality fromInt(int i) { return val[i]; }
                     public Quality next() { return fromInt((ordinal()+1)%amount); }
                    }

一般に、序数の使用は悪い習慣として識別されていますが、避ける方がよいでしょう。
ラファエル

1
static final PcapLinkType[] values  = { DLT_NULL, DLT_EN10MB, DLT_EN3MB, null ...}    

...

public static PcapLinkType  getPcapLinkTypeForInt(int num){    
    try{    
       return values[int];    
    }catch(ArrayIndexOutOfBoundsException e){    
       return DLT_UKNOWN;    
    }    
}    

1
たくさん呼ばれると高価です。配列を更新することを忘れないでください(列挙型が.values()メソッドを定義しているのに、なぜ配列を更新するのですか?)。
dty 2011年

@dtyそれはtry / catchですか?多くの値がDLT_UNKNOWNカテゴリに分類される場合、コストが高いと言った方が公平だと思います。
nsfyn55 2011年

1
アレイソリューションが反対票を投じ、マップソリューションが賛成票を投じたのを見て本当に驚いています。私がここで嫌いなのはですが--int、それは明らかにタイプミスです。
18446744073709551615 2012

なるほど:彼らnullDLT_UKNOWN:)の代わりに欲しい
18446744073709551615 2012

1
なぜstatic final values[] = PcapLinkType.values()ですか?
18446744073709551615 2012

0

整数ベースの列挙型をエレガントに処理する方法はありません。ソリューションの代わりに文字列ベースの列挙を使用することを考えるかもしれません。常に好ましい方法ではありませんが、それでも存在します。

public enum Port {
  /**
   * The default port for the push server.
   */
  DEFAULT("443"),

  /**
   * The alternative port that can be used to bypass firewall checks
   * made to the default <i>HTTPS</i> port.
   */
  ALTERNATIVE("2197");

  private final String portString;

  Port(final String portString) {
    this.portString = portString;
  }

  /**
   * Returns the port for given {@link Port} enumeration value.
   * @return The port of the push server host.
   */
  public Integer toInteger() {
    return Integer.parseInt(portString);
  }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.