web-dev-qa-db-ja.com

enumとint / Stringの間の便利なマッピング

有限数の値のみをとることができる変数/パラメータを使用する場合、私は常に次のようにJavaのenumを使用しようとします

public enum BonusType {
  MONTHLY, YEARLY, ONE_OFF
}

私がコード内にいる限り、それはうまく機能します。ただし、同じ目的でプレーンint(またはString)値を使用する他のコードとインターフェイスする必要がある場合があります。または、データが保存されているデータベースとの間で読み取り/書き込みを行う必要があります数値または文字列。

その場合、両方の方法を変換できるように、各列挙値を整数に関連付ける便利な方法が必要です(言い換えると、「可逆列挙」が必要です)。

Enumからintに行くのは簡単です:

public enum BonusType {
  public final int id;

  BonusType(int id) {
    this.id = id;
  }
  MONTHLY(1), YEARLY(2), ONE_OFF(3);
}

その後、BonusType x = MONTHLY; int id = x.id;としてint値にアクセスできます。

ただし、逆、つまりintからenumに移行するための素晴らしい方法はありません。理想的には、

BonusType bt = BonusType.getById(2); 

私が思いつく唯一の解決策は次のとおりです。

  • ルックアップメソッドを列挙に配置します。このメソッドは、BonusType.values()を使用して「int-> enum」マップを埋め、それをキャッシュしてルックアップに使用します。動作しますが、使用する各列挙にこのメソッドを同じようにコピーする必要があります:-(。
  • ルックアップメソッドを静的ユーティリティクラスに入れます。次に、1つの「ルックアップ」メソッドだけが必要になりますが、リフレクションをいじって任意の列挙型で機能するようにする必要があります。

どちらの方法も、このような単純な(?)問題のためにひどく扱いにくいようです。

他のアイデア/洞察はありますか?

105
sleske

http://www.javaspecialists.co.za/archive/Issue113.html

解決策は、列挙定義の一部としてint値を使用して、あなたのものと同様に開始します。次に、ジェネリックベースのルックアップユーティリティを作成します。

public class ReverseEnumMap<V extends Enum<V> & EnumConverter> {
    private Map<Byte, V> map = new HashMap<Byte, V>();
    public ReverseEnumMap(Class<V> valueType) {
        for (V v : valueType.getEnumConstants()) {
            map.put(v.convert(), v);
        }
    }

    public V get(byte num) {
        return map.get(num);
    }
}

このソリューションはすてきで、すべての列挙型が暗黙的にEnumインターフェイスを継承するという事実に基づいているため、「反射をいじる」必要はありません。

36
Jeff

列挙型→int

yourEnum.ordinal()

int→列挙型

EnumType.values()[someInt]

文字列→列挙

EnumType.valueOf(yourString)

列挙型→文字列

yourEnum.name()

サイドノート:
あなたが正しく指摘しているように、ordinal()はバージョンごとに「不安定」である可能性があります。これが、データベースに定数を常に文字列として格納する正確な理由です。 (実際には、MySqlを使用するとき、 MySql enums !として保存します。)

323
aioobe

私はこれをウェブ上で見つけましたが、とても役に立ち、簡単に実装できました。この解決策は私が作ったものではありません

http://www.ajaxonomy.com/2007/Java/making-the-most-of-Java-50-enum-tricks

public enum Status {
 WAITING(0),
 READY(1),
 SKIPPED(-1),
 COMPLETED(5);

 private static final Map<Integer,Status> lookup 
      = new HashMap<Integer,Status>();

 static {
      for(Status s : EnumSet.allOf(Status.class))
           lookup.put(s.getCode(), s);
 }

 private int code;

 private Status(int code) {
      this.code = code;
 }

 public int getCode() { return code; }

 public static Status get(int code) { 
      return lookup.get(code); 
 }

}

27
melqkiades

Java 8のリリースでは、この質問に対する回答は時代遅れになっているようです。

  1. データベースなどのJVMの外部で永続化される場合、序数は不安定であるため、序数を使用しないでください。
  2. キー値を使用して静的マップを作成するのは比較的簡単です。

public enum AccessLevel {
  PRIVATE("private", 0),
  PUBLIC("public", 1),
  DEFAULT("default", 2);

  AccessLevel(final String name, final int value) {
    this.name = name;
    this.value = value;
  }

  private final String name;
  private final int value;

  public String getName() {
    return name;
  }

  public int getValue() {
    return value;
  }

  static final Map<String, AccessLevel> names = Arrays.stream(AccessLevel.values())
      .collect(Collectors.toMap(AccessLevel::getName, Function.identity()));
  static final Map<Integer, AccessLevel> values = Arrays.stream(AccessLevel.values())
      .collect(Collectors.toMap(AccessLevel::getValue, Function.identity()));

  public static AccessLevel fromName(final String name) {
    return names.get(name);
  }

  public static AccessLevel fromValue(final int value) {
    return values.get(value);
  }
}
6
John Meyer

org.Apache.commons.lang.enums.ValuedEnum;

ボイラープレートコードを大量に記述したり、各Enumのコードを複製したりするのを避けるために、代わりにApache Commons LangのValuedEnumを使用しました。

定義

public class NRPEPacketType extends ValuedEnum {    
    public static final NRPEPacketType TYPE_QUERY = new NRPEPacketType( "TYPE_QUERY", 1);
    public static final NRPEPacketType TYPE_RESPONSE = new NRPEPacketType( "TYPE_RESPONSE", 2);

    protected NRPEPacketType(String name, int value) {
        super(name, value);
    }
}

使用法:

int-> ValuedEnum:

NRPEPacketType packetType = 
 (NRPEPacketType) EnumUtils.getEnum(NRPEPacketType.class, 1);
4

おそらく次のようなものを使用できます

interface EnumWithId {
    public int getId();

}


enum Foo implements EnumWithId {

   ...
}

これにより、ユーティリティクラスに反映する必要が減ります。

3
extraneon

このコードでは、永続的かつ集中的な検索のために、使用するメモリまたはプロセスがあり、コンバータ配列をインデックスとして使用してメモリを選択します。お役に立てば幸いです

public enum Test{ 
VALUE_ONE(101, "Im value one"),
VALUE_TWO(215, "Im value two");
private final int number;
private final byte[] desc;

private final static int[] converter = new int[216];
static{
    Test[] st = values();
    for(int i=0;i<st.length;i++){
        cv[st[i].number]=i;
    }
}

Test(int value, byte[] description) {
    this.number = value;
    this.desc = description;
}   
public int value() {
    return this.number;
}
public byte[] description(){
    return this.desc;
}

public static String description(int value) {
    return values()[converter[rps]].desc;
}

public static Test fromValue(int value){
return values()[converter[rps]];
}
}

インターフェイスを使用して、上司を表示します。

public interface SleskeEnum {
    int id();

    SleskeEnum[] getValues();

}

public enum BonusType implements SleskeEnum {


  MONTHLY(1), YEARLY(2), ONE_OFF(3);

  public final int id;

  BonusType(int id) {
    this.id = id;
  }

  public SleskeEnum[] getValues() {
    return values();
  }

  public int id() { return id; }


}

public class Utils {

  public static SleskeEnum getById(SleskeEnum type, int id) {
      for(SleskeEnum t : type.getValues())
          if(t.id() == id) return t;
      throw new IllegalArgumentException("BonusType does not accept id " + id);
  }

  public static void main(String[] args) {

      BonusType shouldBeMonthly = (BonusType)getById(BonusType.MONTHLY,1);
      System.out.println(shouldBeMonthly == BonusType.MONTHLY);

      BonusType shouldBeMonthly2 = (BonusType)getById(BonusType.MONTHLY,1);
      System.out.println(shouldBeMonthly2 == BonusType.YEARLY);

      BonusType shouldBeYearly = (BonusType)getById(BonusType.MONTHLY,2);
      System.out.println(shouldBeYearly  == BonusType.YEARLY);

      BonusType shouldBeOneOff = (BonusType)getById(BonusType.MONTHLY,3);
      System.out.println(shouldBeOneOff == BonusType.ONE_OFF);

      BonusType shouldException = (BonusType)getById(BonusType.MONTHLY,4);
  }
}

そして結果:

C:\Documents and Settings\user\My Documents>Java Utils
true
false
true
true
Exception in thread "main" Java.lang.IllegalArgumentException: BonusType does not accept id 4
        at Utils.getById(Utils.Java:6)
        at Utils.main(Utils.Java:23)

C:\Documents and Settings\user\My Documents>
2
corsiKa

.ordinal()values()[i]は両方とも列挙型の順序に依存しているため不安定です。したがって、enumの順序を変更したり、プログラムの一部を追加または削除すると、プログラムが破損します。

以下は、enumとintの間をマップするためのシンプルで効果的な方法です。

public enum Action {
    ROTATE_RIGHT(0), ROTATE_LEFT(1), RIGHT(2), LEFT(3), UP(4), DOWN(5);

    public final int id;
    Action(int id) {
        this.id = id;
    }

    public static Action get(int id){
        for (Action a: Action.values()) {
            if (a.id == id)
                return a;
        }
        throw new IllegalArgumentException("Invalid id");
    }
}

文字列に適用することは難しくありません。

2
hrzafer

逆列挙の非常にクリーンな使用例

ステップ1interface EnumConverterを定義する

public interface EnumConverter <E extends Enum<E> & EnumConverter<E>> {
    public String convert();
    E convert(String pKey);
}

ステップ2

クラス名ReverseEnumMapを作成する

import Java.util.HashMap;
import Java.util.Map;

public class ReverseEnumMap<V extends Enum<V> & EnumConverter<V>> {
    private Map<String, V> map = new HashMap<String, V>();

    public ReverseEnumMap(Class<V> valueType) {
        for (V v : valueType.getEnumConstants()) {
            map.put(v.convert(), v);
        }
    }

    public V get(String pKey) {
        return map.get(pKey);
    }
}

ステップ3

Enumクラスに移動し、implementEnumConverter<ContentType>を使用し、もちろんインターフェースメソッドをオーバーライドします。また、静的なReverseEnumMapを初期化する必要があります。

public enum ContentType implements EnumConverter<ContentType> {
    VIDEO("Video"), GAME("Game"), TEST("Test"), IMAGE("Image");

    private static ReverseEnumMap<ContentType> map = new ReverseEnumMap<ContentType>(ContentType.class);

    private final String mName;

    ContentType(String pName) {
        this.mName = pName;
    }

    String value() {
        return this.mName;
    }

    @Override
    public String convert() {
        return this.mName;
    }

    @Override
    public ContentType convert(String pKey) {
        return map.get(pKey);
    }
}

ステップ4

Communicationクラスファイルを作成し、その新しいメソッドを呼び出してEnumStringに、StringEnumに変換します。説明のためにmainメソッドを配置しました。

public class Communication<E extends Enum<E> & EnumConverter<E>> {
    private final E enumSample;

    public Communication(E enumSample) {
        this.enumSample = enumSample;
    }

    public String resolveEnumToStringValue(E e) {
        return e.convert();
    }

    public E resolveStringEnumConstant(String pName) {
        return enumSample.convert(pName);
    }

//Should not put main method here... just for explanation purpose. 
    public static void main(String... are) {
        Communication<ContentType> comm = new Communication<ContentType>(ContentType.GAME);
        comm.resolveEnumToStringValue(ContentType.GAME); //return Game
        comm.resolveStringEnumConstant("Game"); //return GAME (Enum)
    }
}

詳細な説明はここをクリック

2
AZ_

Javaで同じかどうかはわかりませんが、Cの列挙型も自動的に整数にマッピングされるため、型または整数を使用してアクセスできます。あなたはまだ整数でそれにアクセスしようとしましたか?

1
Detra83

完全を期すために、ここでは、列挙型からインデックスによって列挙値を取得する一般的なアプローチを示します。私の意図は、メソッドの外観を Enum.valueOf(Class、String) のようにすることでした。 Fyi、このメソッドを here からコピーしました。

インデックス関連の問題(ここで詳細に説明しています)は引き続き適用されます。

/**
 * Returns the {@link Enum} instance for a given ordinal.
 * This method is the index based alternative
 * to {@link Enum#valueOf(Class, String)}, which
 * requires the name of an instance.
 * 
 * @param <E> the enum type
 * @param type the enum class object
 * @param ordinal the index of the enum instance
 * @throws IndexOutOfBoundsException if ordinal < 0 || ordinal >= enums.length
 * @return the enum instance with the given ordinal
 */
public static <E extends Enum<E>> E valueOf(Class<E> type, int ordinal) {
    Preconditions.checkNotNull(type, "Type");
    final E[] enums = type.getEnumConstants();
    Preconditions.checkElementIndex(ordinal, enums.length, "ordinal");
    return enums[ordinal];
}
1
whiskeysierra

本当に素晴らしい質問です:-)私は先ほど、ファーガソン氏に似たソリューションを使用しました。逆コンパイルされた列挙型は次のようになります。

final class BonusType extends Enum
{

    private BonusType(String s, int i, int id)
    {
        super(s, i);
        this.id = id;
    }

    public static BonusType[] values()
    {
        BonusType abonustype[];
        int i;
        BonusType abonustype1[];
        System.arraycopy(abonustype = ENUM$VALUES, 0, abonustype1 = new BonusType[i = abonustype.length], 0, i);
        return abonustype1;
    }

    public static BonusType valueOf(String s)
    {
        return (BonusType)Enum.valueOf(BonusType, s);
    }

    public static final BonusType MONTHLY;
    public static final BonusType YEARLY;
    public static final BonusType ONE_OFF;
    public final int id;
    private static final BonusType ENUM$VALUES[];

    static 
    {
        MONTHLY = new BonusType("MONTHLY", 0, 1);
        YEARLY = new BonusType("YEARLY", 1, 2);
        ONE_OFF = new BonusType("ONE_OFF", 2, 3);
        ENUM$VALUES = (new BonusType[] {
            MONTHLY, YEARLY, ONE_OFF
        });
    }
}

これを見ると、ordinal()が不安定である理由が明らかです。 super(s, i);iです。また、既に列挙したものよりもエレガントなソリューションを考えることができると悲観的です。すべての列挙型は、最終クラスとしてのクラスです。

1
Lachezar Balev
Int -->String :

public enum Country {

    US("US",0),
    UK("UK",2),
    DE("DE",1);


    private static Map<Integer, String> domainToCountryMapping; 
    private String country;
    private int domain;

    private Country(String country,int domain){
        this.country=country.toUpperCase();
        this.domain=domain;
    }

    public String getCountry(){
        return country;
    }


    public static String getCountry(String domain) {
        if (domainToCountryMapping == null) {
            initMapping();
        }

        if(domainToCountryMapping.get(domain)!=null){
            return domainToCountryMapping.get(domain);
        }else{
            return "US";
        }

    }

     private static void initMapping() {
         domainToCountryMapping = new HashMap<Integer, String>();
            for (Country s : values()) {
                domainToCountryMapping.put(s.domain, s.country);
            }
        }
0
Ran Adler

与えられた:

public enum BonusType {MONTHLY(0)、YEARLY(1)、ONE_OFF(2)}

BonusTypeボーナス= YEARLY;

System.out.println(bonus.Ordinal()+ ":" +ボーナス)

出力:1:YEARLY

0
David Urry

受け入れられた答えが自己完結していないという理由だけで:

サポートコード:

public interface EnumWithCode<E extends Enum<E> & EnumWithCode<E>> {

    public Integer getCode();

    E fromCode(Integer code);
}


public class EnumWithCodeMap<V extends Enum<V> & EnumWithCode<V>> {

    private final HashMap<Integer, V> _map = new HashMap<Integer, V>();

    public EnumWithCodeMap(Class<V> valueType) {
        for( V v : valueType.getEnumConstants() )
            _map.put(v.getCode(), v);
    }

    public V get(Integer num) {
        return _map.get(num);
    }
}

使用例:

public enum State implements EnumWithCode<State> {
    NOT_STARTED(0), STARTED(1), ENDED(2);

    private static final EnumWithCodeMap<State> map = new EnumWithCodeMap<State>(
            State.class);

    private final int code;

    private State(int code) {
        this.code = code;
    }

    @Override
    public Integer getCode() {
        return code;
    }

    @Override
    public State fromCode(Integer code) {
        return map.get(code);
    }

}
0
leonbloy

汎用的なアプローチを使用したかったため、別のものが必要でした。バイト配列との間で列挙型を読み取ります。これが私が思いつく場所です:

public interface EnumConverter {
    public Number convert();
}



public class ByteArrayConverter {
@SuppressWarnings("unchecked")
public static Enum<?> convertToEnum(byte[] values, Class<?> fieldType, NumberSystem numberSystem) throws InvalidDataException {
    if (values == null || values.length == 0) {
        final String message = "The values parameter must contain the value";
        throw new IllegalArgumentException(message);
    }

    if (!dtoFieldType.isEnum()) {
        final String message = "dtoFieldType must be an Enum.";
        throw new IllegalArgumentException(message);
    }

    if (!EnumConverter.class.isAssignableFrom(fieldType)) {
        final String message = "fieldType must implement the EnumConverter interface.";
        throw new IllegalArgumentException(message);
    }

    Enum<?> result = null;
    Integer enumValue = (Integer) convertToType(values, Integer.class, numberSystem); // Our enum's use Integer or Byte for the value field.

    for (Object enumConstant : fieldType.getEnumConstants()) {
        Number ev = ((EnumConverter) enumConstant).convert();

        if (enumValue.equals(ev)) {
            result = (Enum<?>) enumConstant;
            break;
        }
    }

    if (result == null) {
        throw new EnumConstantNotPresentException((Class<? extends Enum>) fieldType, enumValue.toString());
    }

    return result;
}

public static byte[] convertEnumToBytes(Enum<?> value, int requiredLength, NumberSystem numberSystem) throws InvalidDataException {
    if (!(value instanceof EnumConverter)) {
        final String message = "dtoFieldType must implement the EnumConverter interface.";
        throw new IllegalArgumentException(message);
    }

    Number enumValue = ((EnumConverter) value).convert();
    byte[] result = convertToBytes(enumValue, requiredLength, numberSystem);
    return result;
}

public static Object convertToType(byte[] values, Class<?> type, NumberSystem numberSystem) throws InvalidDataException {
    // some logic to convert the byte array supplied by the values param to an Object.
}

public static byte[] convertToBytes(Object value, int requiredLength, NumberSystem numberSystem) throws InvalidDataException {
    // some logic to convert the Object supplied by the'value' param to a byte array.
}
}

列挙型の例:

public enum EnumIntegerMock implements EnumConverter {
    VALUE0(0), VALUE1(1), VALUE2(2);

    private final int value;

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

public Integer convert() {
    return value;
}

}

public enum EnumByteMock implements EnumConverter {
    VALUE0(0), VALUE1(1), VALUE2(2);

    private final byte value;

    private EnumByteMock(int value) {
        this.value = (byte) value;
    }

    public Byte convert() {
        return value;
    }
}
0