Int를 Java로 열거


333

다음 열거 형이 주어지면 Java에서 Int를 열거 형으로 캐스팅하는 올바른 방법은 무엇입니까?

public enum MyEnum
{
    EnumValue1,
    EnumValue2
}


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

@RyuS. 이 질문과 중복되지 않습니다
Mark Rotteveel

답변:


586

시도 MyEnum.values()[x]x이어야 0또는 1그 열거에 대한 올바른 순서, 즉.

Java 열거 형은 실제로 클래스이며 따라서 열거 형 값은 객체이므로 열거 형 int또는 캐스트 Integer형을 캐스팅 할 수 없습니다 .


116
+1 : MyEnum.values()고가 로 캐시하고 싶을 수도 있습니다 . 즉, 수백 번 호출하면
피터로 레이

6
@PeterLawrey 완벽 함을 위해 왜 느려 야하는지 설명 할 수 있습니까? 분명한 이유가 없습니다.
Tarrasch

48
배열은 변경 가능하므로 @Tarrasch, values ​​()는 변경이 발생할 경우를 대비하여 요소 배열의 복사본을 반환해야합니다. 매번이 사본을 작성하는 것은 상대적으로 비쌉니다.
Peter Lawrey

9
@PeterLawrey 나는 최근에 많은 걸쇠를 사용했습니다 (모든 것이 불변의 곳입니다)! 명확하고 간결한 설명에 감사드립니다. :)
Tarrasch

'x'를 얻는 방법?
Beeing Jk

161

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];
길리 Nachum

23
@GiliNachum은 이상한 방식으로 말했지만이 솔루션의 문제는 유지 관리 가능성입니다. 이는 DRY 원칙에 위배됩니다. 즉, 열거 형 값이 변경 될 때마다 (재정렬, 값 추가, 값 제거) 스위치 명령문이 동시에 업데이트되어야합니다. Gili의 의견에 따르면 Java는 열거 형 값 목록과 일관성을 유지해야하며 열거 형 값을 변경해도 해당 접근법에는 전혀 영향을 미치지 않습니다.
Dandre Allison

3
@LorenzoPolidori, 왜 MyEnum.values()[x]비싼 운영 이라고 생각하는지 설명해 주시겠습니까? 나는 그것이 세부적으로 어떻게 작동하는지 모르지만, 배열의 요소에 액세스하는 것은 일관된 시간이 될 것 같습니다. 어레이를 구축해야하는 경우 O (n) 시간이 걸리며 이는 솔루션과 동일한 실행 시간입니다.
brunsgaard

1
@brunsgaard values()배열은 변경 가능하므로 동일한 배열을 여러 번 반환하는 것이 안전하지 않기 때문에 매번 새로운 배열을 생성 한다고 가정 합니다. 스위치 문은 반드시 O (n) 일 필요는 없으며 테이블을 점프하도록 컴파일 할 수 있습니다. 로렌조의 주장은 정당 해 보인다.
MikeFHay

1
@Johnson Gili의 버전은 Enum 선언의 변경 이외의 추가 코드 변경이 필요하지 않습니다. 열거 형에 새 항목을 추가 해도 변경 사항없이 열거 형의 항목 myEnum = myEnumValues[i]이 계속 반환 i됩니다.
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

또한 이것은 0 .. (count-1)의 기본 서수를 사용하지 않고 정수 값의 스파 스 (또는 반복) 세트를 원할 경우 작동하는 유일한 대답 인 것 같습니다. 네트워크와 같은 기존 코드와 상호 작용하는 경우 중요 할 수 있습니다.
benkc

위의 답변에서와 같이 값을 호출 할 때마다 메모리 할당 및 배열 복사를 피하기 위해 values ​​() 결과를 캐싱하는 것이 좋습니다.
benkc

1
그리고 열거 형의 길이에 따라 매번 선형 검색을 수행하는 대신 HashMap을 만들거나이 검색에 이진 검색 또는 무언가를 사용하려고 할 수 있습니다.
benkc

2
이것은 int를 열거 형으로 변환하는 올바른 방법과 모범 사례이어야하며 공개 정적 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
이것이 내가 지금 사용하는 솔루션입니다. 그러나 IMHO 필드 values에 method와 같은 이름을 지정 하지 않으면 혼동이 줄어 듭니다 values(). cachedValues필드 이름으로 사용 합니다.
ToolmakerSteve 2016 년

2
효율적이고 우아한; 내 프로젝트에 복사하여 붙여 넣습니다.) 내가 변경 한 유일한 것은 fromInt (int i)입니다. fromInt (int i)는 int (int i)에서 호출되었습니다.
pipedreambomb

1
왜 처음부터 초기화하지 않습니까? 첫 번째 히트를 기다리는 이유는 무엇입니까?
카이저

9

Java 열거 형에는 C ++에서와 같은 종류의 열거 형 매핑이 없습니다.

즉, 모든 열거 형에는 values가능한 열거 형 값의 배열을 반환하는 메서드가 있으므로

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

작동해야합니다. 그것은 조금 불쾌하고 가능한 경우 s 에서 ints로 Enum또는 그 반대로 변환하지 않는 것이 좋습니다 .


9

이것은 일반적으로 수행되는 것이 아니므로 다시 고려할 것입니다. 그러나 기본 작업은 int-> EnumType.values ​​() [intNum]을 사용하는 enum, enumInst.ordinal ()을 사용하는 enum-> int입니다.

그러나 values ​​() 구현은 배열의 사본을 제공하는 것 외에는 선택의 여지가 없기 때문에 (자바 배열은 읽기 전용이 아닙니다) EnumMap을 사용하여 enum-> int 매핑을 캐시하는 것이 더 좋습니다.


2
다시 "이것은 일반적으로 수행되는 작업이 아닙니다": 유용한 경우 : enum은 데이터베이스에 저장된 int 값에 해당합니다.
ToolmakerSteve

@ToolmakerSteve는 매핑이 필요하다는 것을 전적으로 옳습니다. 그러나 그런 종류의 인코딩을 OR 매퍼 또는 툴킷 / 라이브러리에두고 싶습니까?
Dilum Ranatunga


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 클래스의 정적 멤버로 캐시 할 수 있습니다.


IMHO 메모리 사용에 관심이 있다면 @ossys와 같이 HashMap을 동적으로 만들지 만 캐시가 null 일 때 다른 코드로 만든 다음 사용을 마치면 두 번째 방법을 추가 clearCachedValues합니다 (비공개 필드를 다시 null로 설정). MyEnum.fromInt(i)지도 객체를 전달하는 것보다 이해하기 쉽다고 생각 합니다.
ToolmakerSteve 2016 년

6

다른 사람들을 돕기 위해 여기에 나열되지 않은 선호하는 옵션은 Guava의지도 기능을 사용 합니다 .

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원하는대로 행동.


3
구아바를 사용한다고 언급해야합니다. 또는 스트림을 사용할 수 있습니다.Map<Integer, MyEnum> reverseLookup = Arrays.stream(MyEnum.values()).collect(Collectors.toMap(MyEnum::getValue, Function.identity()));
shmosel

1
getValue()방법 을 정의하고 싶을 수도 있습니다.
shmosel

@shmosel 죄송합니다. getValue 함수를 놓쳤습니다. 감사합니다. 일반 버전을 stackoverflow에 입력한다고해서 항상 팬 아웃되는 것은 아닙니다. 링크와 함께 구아바 사용에 대한 의견을 추가했습니다. 구아바 방법을 스트림보다 선호하십시오.
차드 베 푸스

4

values()열거 형을 반복하고 열거 형의 정수 값을 다음 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 : 당신이 최대 값을 필요로하는 경우, 예를 들어, 당신은 x.ordinal 비교할 수있다 () y.ordinal에 () 및 반환 X 또는 Y를 대응. 이러한 비교를 의미있게하려면 값을 다시 정렬해야 할 수도 있습니다.

그것이 가능하지 않으면 MyEnum.values()정적 배열에 저장 합니다.


1
DB에서 int를 가져 와서 매우 일반적인 작업이라고 생각하는 Enum으로 변환하려면 int-> enum conversion, IMO가 필요합니다.
Yuki Inoue

0

이것은 의사와 같은 대답이지만 변경 가능한 배열의 문제를 제거하는 방법을 보여줍니다. 분기 예측 때문에 이러한 종류의 접근 방식을 먼저 사용하는 경우 효과가 거의 없거나 전혀 없으며 전체 코드는 가변 배열 값 () 함수를 한 번만 호출합니다. 두 변수는 모두 정적이므로이 열거를 사용할 때마다 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 범위를 벗어난 것이 거의 없으면 예외가 훨씬 더 낫다고 생각합니다.
Zoso

당신의 믿음은 무엇에 기초하고 있습니까?
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.