열거 형 서수에서 열거 형으로 변환


316

나는 ReportTypeEnum모든 클래스의 메소드간에 전달 되는 열거 형 을 가지고 있지만 URL에 이것을 전달해야 서수 메소드를 사용하여 int 값을 얻습니다. 다른 JSP 페이지에서 가져온 후에 ReportTypeEnum는 계속 전달할 수 있도록 다시 변환해야 합니다.

서수를 어떻게로 변환 할 수 ReportTypeEnum있습니까?

Java 6 SE 사용


1
지금까지는 Java 6 EE가 없습니다 (AFAIK). Java SE 6 및 Java EE 5가 있습니다.
Hosam Aly

답변:


632

서수를 열거 형 represantation으로 변환하려면 다음을 수행하십시오.

ReportTypeEnum value = ReportTypeEnum.values()[ordinal];

배열 범위를 확인하십시오.

호출 할 때마다 values()새로 복제 된 배열 을 반환하므로 성능에 부정적인 영향을 줄 수 있습니다. 배열이 자주 호출되는 경우 배열을 캐시 할 수 있습니다.

캐시하는 방법에 대한 코드 예제values() .


이 답변은 댓글 안에 제공된 피드백을 포함하도록 편집되었습니다


이 솔루션을 구현했지만 효과가 없습니다. 열거 형이 추가 된 순서와 일치하는 서수 값을 반환하지 않습니다. 나는
이것이이

@IcesDante : 서수는 소스의 열거 형 값의 순서와 일치해야합니다. 다른 행동을 관찰하면 다른 것이 잘못되었을 것입니다. 그러나 위의 답변은 다른 답변에 명시된 모든 이유로 차선책입니다.
Joachim Sauer

@JoachimSauer 어쩌면 IcedDante는 서 수가 열거 형 소스의 이전 버전에 의해 생성되고 저장된 경우 일치하지 않을 수 있음을 의미합니다.
LarsH

137

이것은 거의 확실히 나쁜 생각 입니다. 물론 서 수가 사실상 유지되지 않는 경우 (예 : 누군가가 URL을 북마크에 추가했기 때문에) enum이는 향후 순서를 항상 유지해야한다는 것을 의미합니다 . 이는 코드 관리자에게 명확하지 않을 수 있습니다.

대신 인코딩을 enum사용하고 myEnumValue.name()(를 통해 디코딩 ReportTypeEnum.valueOf(s)) 왜 안 됩니까?


24
열거 형의 이름을 변경하면 (그러나 순서를 유지하면) 어떻게됩니까?
Arne Evertsson 2009

6
@ Arne-이것은 경험이없는 사람이 와서 value시작하거나 올바른 알파벳 / 논리 위치 에을 추가하는 것보다 훨씬 적다고 생각합니다 . (논리적으로 나는 예를 들어 TimeUnit값이 논리적 인 위치를 의미 함 )
oxbow_lakes

7
나는 열거 형의 이름보다는 열거 형 순서를 강제하는 것을 선호합니다 ... 이것은 데이터베이스에 열거 형의 이름보다는 서수를 저장하는 것을 선호하는 이유입니다. 또한 문자열보다는 int 조작을 사용하는 것이 좋습니다.

8
나는 동의한다. 공개 API에서 Enum의 이름을 변경하면 이전 버전과의 호환성이 손상되지만 순서를 변경하지는 않습니다. 따라서 이름을 "키"로 사용하는 것이 더 합리적입니다.
Noel

3
서수를 저장하면 아이디어를 다른 언어로 쉽게 번역 할 수 있습니다. C로 컴포넌트를 작성해야한다면 어떻게해야합니까?
QED

94

values()많이 사용할 예정이라면 :

enum Suit {
   Hearts, Diamonds, Spades, Clubs;
   public static final Suit values[] = values();
}

한편 where.java :

Suit suit = Suit.values[ordinal];

배열 경계를 염두에 두십시오.


3
+1 이것은 특히 android.os.Message에서 서수를 전달할 수 있기 때문에 가장 좋은 솔루션 IMHO입니다.
likejudo

9
배열이 변경 가능 하기 때문에 이것은 매우 걱정 입니다. values ​​[]가 최종이지만 Suit.values[0] = Suit.Diamonds;코드의 어딘가를 막지는 않습니다 . 이상적으로는 결코 발생 하지 않지만 변경 가능한 필드를 노출시키지 않는 전반적인 원칙은 여전히 유효합니다. 이 방법의 경우 Collections.unmodifiableList대신 등을 사용 하는 것이 좋습니다.
Mshnik

어떻습니까-private static final Suit values ​​[] = values ​​(); public static Suit [] getValues ​​() {반환 값; }
Pratyush

4
@Pratyush는 배열 변수를 변경할 수 없지만 내용은 변경할 수 없습니다. 여전히 getValues ​​() [0] = somethingElse;를 수행 할 수 있습니다.
Calabacin

따라서 포인트가 Suit values[]직접 또는 간접적으로 노출되지는 않습니다 (어떻게 언급 되었는가 getValues()), 나는 ordinal인수가 어떻게 전달되고 Suit표현을 반환 해야하는지 공개 메소드로 해결합니다 Suit values[]. 여기서 요점은 (처음부터 질문의 타일) 열거 형 서수에서 열거 형을 만들었습니다
Manuel Jordan

13

나는 서수를 사용하는 것이 아마도 나쁜 생각이라는 대부분의 사람들에게 동의합니다. 나는 일반적으로 열거 형에 DB 값을 취할 수있는 개인 생성자를 제공하여 fromDbValueJan의 답변과 비슷한 정적 함수 를 생성 하여이 문제를 해결합니다 .

public enum ReportTypeEnum {
    R1(1),
    R2(2),
    R3(3),
    R4(4),
    R5(5),
    R6(6),
    R7(7),
    R8(8);

    private static Logger log = LoggerFactory.getLogger(ReportEnumType.class);  
    private static Map<Integer, ReportTypeEnum> lookup;
    private Integer dbValue;

    private ReportTypeEnum(Integer dbValue) {
        this.dbValue = dbValue;
    }


    static {
        try {
            ReportTypeEnum[] vals = ReportTypeEnum.values();
            lookup = new HashMap<Integer, ReportTypeEnum>(vals.length);

            for (ReportTypeEnum  rpt: vals)
                lookup.put(rpt.getDbValue(), rpt);
         }
         catch (Exception e) {
             // Careful, if any exception is thrown out of a static block, the class
             // won't be initialized
             log.error("Unexpected exception initializing " + ReportTypeEnum.class, e);
         }
    }

    public static ReportTypeEnum fromDbValue(Integer dbValue) {
        return lookup.get(dbValue);
    }

    public Integer getDbValue() {
        return this.dbValue;
    }

}

이제 조회를 변경하지 않고 순서를 변경할 수 있으며 그 반대도 마찬가지입니다.


이것이 정답입니다. (직후 코드 변경으로 인해) 다른 직접적이지만 잠재적으로 유효하지 않은 답변과 비교할 때 점수가 너무 적다는 것에 놀랐습니다.
Calabacin

8

당신은 할 수 정적 조회 테이블을 사용합니다 :

public enum Suit {
  spades, hearts, diamonds, clubs;

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

  static{
    int ordinal = 0;
    for (Suit suit : EnumSet.allOf(Suit.class)) {
      lookup.put(ordinal, suit);
      ordinal+= 1;
    }
  }

  public Suit fromOrdinal(int ordinal) {
    return lookup.get(ordinal);
  }
}

3
Enums 도 참조하십시오 .
trashgod

11
와! 와우! 물론 이것은 깔끔하지만 ... 알다시피-내 내부의 C 프로그래머는 당신이 본격적으로 해시 맵을 할당하고 그 내부에서 조회를 수행하여 본질적으로 4 개의 상수, 스페이드, 하트, 다이아몬드와 클럽! AC 프로그래머는 각각에 대해 1 바이트를 할당합니다 : 'const char CLUBS = 0;' etc ... 그렇습니다. HashMap 조회는 O (1)이지만 HashMap의 메모리 및 CPU 오버 헤드는이 경우 .values ​​()를 직접 호출하는 것보다 훨씬 느리고 리소스가 부족합니다! 사람들이 이런 식으로 글을 쓰면 자바가 그런 메모리 호그 인 것도 당연합니다.
Leszek

2
모든 프로그램이 트리플 A 게임의 성능을 요구하는 것은 아닙니다. 대부분의 경우 유형 안전성, 가독성, 유지 관리 성, 플랫폼 간 지원, 가비지 수집 등을 위해 메모리와 CPU를 거래하는 것은 정당합니다. 더 높은 수준의 언어가 존재합니다.
Jan

3
그러나 키 범위가 항상 0...(n-1)인 경우 배열은 코드가 적고 읽기 쉽습니다. 성능 향상은 보너스 일뿐입니다. private static final Suit[] VALUES = values();그리고 public Suit fromOrdinal(int ordinal) { return VALUES[ordinal]; }. 추가 이점 : 자동으로 null을 반환하지 않고 잘못된 서수에서 즉시 충돌합니다. (항상 이점은 아니지만 종종.)
Thomas

4

이것이 내가 사용하는 것입니다. 위의 간단한 솔루션보다 훨씬 덜 "효율적"이라고 주장하지 않습니다. 위의 솔루션에서 유효하지 않은 서수 값을 사용하는 경우 "ArrayIndexOutOfBounds"보다 훨씬 명확한 예외 메시지를 제공합니다.

EnumSet javadoc이 반복자가 요소를 자연 순서로 반환하도록 지정한다는 사실을 이용합니다. 정확하지 않은 주장이 있습니다.

JUnit4 테스트는 사용 방법을 보여줍니다.

 /**
 * convert ordinal to Enum
 * @param clzz may not be null
 * @param ordinal
 * @return e with e.ordinal( ) == ordinal
 * @throws IllegalArgumentException if ordinal out of range
 */
public static <E extends Enum<E> > E lookupEnum(Class<E> clzz, int ordinal) {
    EnumSet<E> set = EnumSet.allOf(clzz);
    if (ordinal < set.size()) {
        Iterator<E> iter = set.iterator();
        for (int i = 0; i < ordinal; i++) {
            iter.next();
        }
        E rval = iter.next();
        assert(rval.ordinal() == ordinal);
        return rval;
    }
    throw new IllegalArgumentException("Invalid value " + ordinal + " for " + clzz.getName( ) + ", must be < " + set.size());
}

@Test
public void lookupTest( ) {
    java.util.concurrent.TimeUnit tu = lookupEnum(TimeUnit.class, 3);
    System.out.println(tu);
}

1

이것이 Proguard를 사용하여 Android에서 수행하는 작업입니다.

public enum SomeStatus {
    UNINITIALIZED, STATUS_1, RESERVED_1, STATUS_2, RESERVED_2, STATUS_3;//do not change order

    private static SomeStatus[] values = null;
    public static SomeStatus fromInteger(int i) {
        if(SomeStatus.values == null) {
            SomeStatus.values = SomeStatus.values();
        }
        if (i < 0) return SomeStatus.values[0];
        if (i >= SomeStatus.values.length) return SomeStatus.values[0];
        return SomeStatus.values[i];
    }
}

짧고 Proguard에서 예외가 발생할 염려가 없습니다.


1

다음과 같은 간단한 방법을 정의 할 수 있습니다.

public enum Alphabet{
    A,B,C,D;

    public static Alphabet get(int index){
        return Alphabet.values()[index];
    }
}

그리고 그것을 다음과 같이 사용하십시오 :

System.out.println(Alphabet.get(2));

0
public enum Suit implements java.io.Serializable, Comparable<Suit>{
  spades, hearts, diamonds, clubs;
  private static final Suit [] lookup  = Suit.values();
  public Suit fromOrdinal(int ordinal) {
    if(ordinal< 1 || ordinal> 3) return null;
    return lookup[value-1];
  }
}

테스트 클래스

public class MainTest {
    public static void main(String[] args) {
        Suit d3 = Suit.diamonds;
        Suit d3Test = Suit.fromOrdinal(2);
        if(d3.equals(d3Test)){
            System.out.println("Susses");
        }else System.out.println("Fails");
    }
}

더 효율적인 코드가 있다면 우리와 공유해 주셔서 감사합니다. 내 열거 형은 거대하고 끊임없이 수천 번 불립니다.


"if (ordinal <1 || ordinal> 4) return null;"을 의미한다고 생각합니다.
geowar

0

따라서 한 가지 방법은 ExampleEnum valueOfOrdinal = ExampleEnum.values()[ordinal];어느 것이 효과가 있으며 이전에 언급했듯이 ExampleEnum.values()모든 호출에 대해 새로운 복제 된 배열을 반환합니다. 불필요하게 비쌀 수 있습니다. 우리는 배열을 캐싱함으로써 이것을 해결할 수 있습니다.ExampleEnum[] values = values() . 캐시 된 어레이를 수정하는 것도 "위험"합니다. 누군가 쓸 수 ExampleEnum.values[0] = ExampleEnum.type2;있으므로 여분의 복사를 수행하지 않는 접근 자 메소드를 사용하여 비공개로 만들 것입니다.

private enum ExampleEnum{
    type0, type1, type2, type3;
    private static final ExampleEnum[] values = values();
    public static ExampleEnum value(int ord) {
        return values[ord];
    }
}

ExampleEnum.value(ordinal)관련된 enum 값을 얻는 데 사용 합니다.ordinal


-1

모든 열거 형에는 name ()이 있으며, 이는 열거 형 멤버의 이름을 가진 문자열을 제공합니다.

주어진 enum Suit{Heart, Spade, Club, Diamond}, Suit.Heart.name()줄 것이다Heart .

모든 열거 형에는 valueOf() 역 연산을 수행하기 위해 열거 형과 문자열을 사용 메소드 있습니다.

Enum.valueOf(Suit.class, "Heart")을 반환합니다 Suit.Heart.

누구나 서수를 사용하는 이유는 저 밖에 있습니다. 다른 개발자가 일부 코드가 서수 값에 의존한다는 것을 알지 못하기 때문에 열거 형 멤버가 변경되면 나노 초가 빠를 수 있지만 안전하지는 않습니다 (특히 질문에 인용 된 JSP 페이지에서 네트워크 및 데이터베이스 오버 헤드가 완전히 지배합니다) 문자열에 정수를 사용하지 않는 시간).


2
정수를 비교하는 것이 문자열을 비교하는 것보다 훨씬 빠르기 때문에?
HighCommander4

2
그러나 누군가 열거 형을 수정하면 서 수가 변경됩니다 (멤버 추가 / 재정렬). 때로는 네트워크 지연 시간이 정수 배열 (문자열)과 단일 정수를 비교하는 것의 차이의 1,000,000 배인 JSP 페이지에서 속도가 아니라 안전에 관한 것입니다.
Tony BenBrahim

toString을 재정의 할 수 있으므로 열거 형 이름을 반환하지 않을 수 있습니다. 메소드 이름 ()은 열거 이름을 제공하는 것입니다 (최종)
gerardw

코드 주석 및 응용 프로그램 버전도 중요합니다 (파일 형식이 이보다 더 단순하고 작을 수 있음)
joe pelletier

oxbow_lakes의 답변과 본질적으로 동일한 것을 권장 할 때 왜 이것이 다운 투표되었는지 확실하지 않습니다. 서수를 사용하는 것보다 확실히 안전합니다.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.