정수 값을 일치하는 Java Enum으로 변환


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

Integer를 열거 형에 매핑하는 클래스에 정적 맵을 추가하여 수동으로 수행해야합니다.

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

1
allOf는 세트에서만 사용할 수 있습니다
MeBigFatGuy 2011 년

1
또한 EnumMap열거 형을 키로 사용합니다. 이 경우 OP는 열거 형을 값으로 원합니다.
dty 2011 년

8
이것은 많은 불필요한 오버 헤드처럼 보입니다. 실제로 이러한 유형의 작업이 필요한 사람들은 스트림 / 소켓에서 쓰고 / 읽기 때문에 고성능이 필요할 것입니다.이 경우 캐싱 values()(열거 형 값이 순차적 인 경우) 또는 간단한 switch문이이 방법을 쉽게 이길 것입니다. . 항목이 몇 개만있는 경우 Enum단순히 switch명령문 을 업데이트 할 필요가 없다는 편의를 위해 HashMap의 오버 헤드를 추가하는 것은 의미가 없습니다 . 이 방법은 더 우아해 보이지만 낭비이기도합니다.
crush

30

이 정적 방법이다 되고 문서화,하지만 당신이 그것을 기대하지 여기서 http://docs.oracle.com/javase/tutorial/java/javaOO/enum.htmlvalues()

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

1
동의하지 마십시오. 나는 문서에 관계없이 결코 의미가 없다고 믿습니다 . enum을 직접 구현하더라도 서수를 사용해서는 안됩니다. 악취가 나고 오류가 발생하기 쉽습니다. 내가 아니라 전문가하지만 :) 여호수아 블로흐과 논쟁하지 않을는
stevo.mit

4
@ stevo.mit는 Java 8의 새로운 enum java.time.Month를 살펴 봅니다. Month.of (int) 정적 메서드는 Joshua Bloch가 "절대"하지 말아야한다고 말한 작업을 정확히 수행합니다. 서수에 따라 Month를 반환합니다.
Klitos Kyriacou

1
@ stevo.mit 정렬 된 열거 형정렬되지 않은 열거 형이 있습니다. (그리고 비트 마스크 열거 형도 마찬가지입니다.) 단순히 "열거 형"이라고 말하는 것은 잘못된 것입니다. 표현 적 의미가 무엇인지에 대한 결정은 작업하는 추상화 수준을 기반으로해야합니다 . 구현 세부 사항 (하위 수준의 표현 적 수단) 또는 사용 가정 (상위 수준의 표현 적 수단)을 사용하는 것은 실제로 올바르지 않습니다. " 절대 "에 관해서 는 항상 어떤 맥락이 있기 때문에 인간의 언어에서는 결코 결코 의미가 없습니다. (보통 애플리케이션 프로그래밍에서는 절대 ...) BTW, programering.com/a/MzNxQjMwATM.html
18446744073709551615

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

@dty O (N) N = 200 - 난 그 문제 여러가지로 생각
Bozho

7
얼마나 자주 호출되는지에 대한 느낌이없는 완전히 어리석은 진술입니다. 한 번만 호출하면 괜찮습니다. 10Ge 네트워크에서 지나가는 모든 패킷에 대해 호출되는 경우 알고리즘을 200 배 더 빠르게 만드는 것이 매우 중요합니다. 따라서 "if call a lot"로 내 진술을
검증

10

이와 같은 작업을 수행하여 모든 항목을 컬렉션에 자동으로 등록하여 정수를 해당 열거 형으로 쉽게 변환 할 수 있습니다. (BTW, enum 생성자에서 맵에 추가하는 것은 허용되지 않습니다 . 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 */

당신은 항상 연속하지 주석 / * 싹둑, 200 개 이상 열거를 놓친 * /.
MeBigFatGuy

다만 경우에 대비하여 열거 값이 0에서 이행 성이며,이 나쁜 관행입니다
cuasodayleo

4

@MeBigFatGuy가 말했듯이 static {...}블록이 values()컬렉션에 대해 루프를 사용 하도록 만들 수 있다는 점을 제외하고는 다음과 같습니다.

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

4

나는이 질문이 몇 년 전이라는 것을 알고 있지만, 그 동안 Java 8은 우리에게 Optional , 나는 (그것을 사용하여 솔루션을 제공 할 거라고 생각 StreamCollectors) :

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_UNKNOWNnullDLT_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새 열거 형을 추가하는 경우 해당 문에 항목을 추가하는 것을 잊지 마십시오 . 이상적이지 않습니다, IMHO.
dty

1
@dty 그렇다면 HashMap의 오버 헤드가 switch 문에 새 케이스를 추가해야하는 필요성보다 더 크다고 생각하십니까?
crush

1
나는 실수하지 않도록 도와주는 코드를 작성하는 편이 좋으며 따라서 해시 조회의 마이크로 성능에 초점을 맞추기 전에 정확할 가능성이 더 높습니다.
dty

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

@dty는 try / catch입니까? 많은 값이 DLT_UNKNOWN 범주에 속하면 비싸다고 말하는 것이 더 공평하다고 생각합니다.
nsfyn55 2011 년

1
어레이 솔루션이 투표를 거부하고 맵 솔루션이 투표를하는 것을보고 정말 놀랐습니다. 내가 여기서 싫어하는 것은 --int이지만 분명히 오타입니다.
18446744073709551615

나는 참조 : 그들이 원하는 null대신에 DLT_UKNOWN:)
18,446,744,073,709,551,615

1
왜 안돼 static final values[] = PcapLinkType.values()?
18446744073709551615

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.