IntをキャストしてJavaのenumにする
次のような列挙型の場合、IntをJavaの列挙型にキャストする正しい方法は何ですか?
public enum MyEnum
{
EnumValue1,
EnumValue2
}
MyEnum enumValue = (MyEnum) x; //Doesn't work???
MyEnum.values()[x]
を試してください。ここでx
は0
または1
、つまりその列挙型の有効な序数でなければなりません。
Javaでは、実際にはenumはクラスであり(したがってenum値はオブジェクトです)、したがってint
やInteger
をenumにキャストすることはできません。
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;
}
}
整数値を与えたい場合は、以下のような構造を使用できます。
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;
}
}
あなたはこのように試すことができます。
要素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;
}
}
IntをintにしてこのEnumを取得しましょう。
MyEnum myEnum = MyEnum.fromId(5);
値をキャッシュして単純な静的アクセスメソッドを作成します。
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];
}
}
Javaのenumは、C++の場合と同じようなenum-to-intマッピングを持っていません。
とはいえ、すべてのenumは可能なenum値の配列を返すvalues
メソッドを持っています。
MyEnum enumValue = MyEnum.values()[x];
うまくいくはずです。それは少し厄介です、そして可能であればint
sからEnum
sへ(またはその逆)変換しようと試みないでください。
これは通常行われていることではないので、私は再考します。ただし、基本的な操作は、EnumType.values()[intNum]を使用したint - > enum、およびenumInst.ordinal()を使用したenum - > intです。
ただし、values()の実装には配列のコピーを渡す以外の選択肢はないため(Java配列は読み取り専用にはなりません)、enum - > intマッピングをキャッシュするためにEnumMapを使用するほうがよいでしょう。
MyEnum enumValue = MyEnum.values()[x];
を使う
これが私が計画している解決策です。これは非連続整数で機能するだけでなく、列挙型の値の基になる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を列挙型に変換するだけなので、Mapを常にメモリに保持する必要はありません。マップを常にアクセス可能にする必要がある場合は、Enumクラスの静的メンバーとしていつでもマップをキャッシュできます。
それが他の人を助ける場合には、ここにリストされていない私が好むオプションは GuavaのMaps機能 を使う:
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
を使うことができます、あるいはあなたのfromInt
はあなたが望むどんな振る舞いでもOptional
を返すことができます。
以下のようにenumのvalues()
を繰り返し処理し、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
これが役に立つことを願っています;)
@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());
}
}
これは医師と同じ答えですが、可変配列の問題を解決する方法を示しています。あなたが最初に分岐予測のためにこの種のアプローチを使うならば、ifはほとんどまたは全くゼロの効果を持ち、コード全体はmutable array values()関数を一度だけ呼び出すだけです。両方の変数は静的なので、この列挙体を使用するたびに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;
}
これは、インデックスが序数値の問題と同期しなくなることに対処する必要があります。
package service.manager;
public enum Command {
PRINT_FOO(0),
PRINT_BAR(2),
PRINT_BAZ(3);
public int intVal;
Command(int intVal){
this.intVal = intVal;
}
/**
* Functionality to ascertain an enum from an int
* The static block initializes the array indexes to correspond with it's according ordinal value
* Simply use Command.values[index] or get the int value by e.g. Command.PRINT_FOO.intVal;
* */
public static Command values[];
static{
int maxVal = -1;
for(Command cmd : Command.values())
if(maxVal < cmd.intVal)
maxVal = cmd.intVal;
values = new Command[maxVal + 1];
for(Command cmd : Command.values())
values[cmd.intVal] = cmd;
}
}
コトリンでは:
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)!!
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;
}
}
}
良い方法は、int
からenum
への変換を避けることです。例えば、最大値が必要なら、x.ordinal()とyを比較します.ordinal()を実行し、それに応じてxまたはyを返します。 (このような比較を意味のあるものにするために、値を並べ替える必要があるかもしれません。)
それが不可能な場合は、MyEnum.values()
を静的配列に格納します。