Tam sayı değerini eşleşen Java Enum değerine dönüştür


87

Bunun gibi bir numaram var:

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;
    }
}

Şimdi harici girdiden bir int alıyorum ve eşleşen girdiyi istiyorum - bir değer yoksa bir istisna atmak uygun, ama tercihen DLT_UNKNOWN bu durumda olmasını isterim .

int val = in.readInt();
PcapLinkType type = ???; /*convert val to a PcapLinkType */

Yanıtlar:


106

Tamsayıları numaralandırmalarla eşleyen sınıfa statik bir harita ekleyerek bunu manuel olarak yapmanız gerekir.

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'nin önerileriyle güncellendi, bu iyi bir fikirdi.
MeBigFatGuy

Umarım kodumu önce bir derleyicide çalıştırmışsındır ... Ben sadece kafamın tepesinden uydurdum. Tekniğin işe yaradığını biliyorum - dün kullandım. Ama kod başka bir makinede ve bu benim geliştirme araçlarına sahip değil.
dty

1
allOf sadece setler için geçerlidir
MeBigFatGuy

1
Ayrıca, EnumMapnumaralandırmaları anahtar olarak kullanır. Bu durumda, OP numaralandırmaları değerler olarak ister.
dty

8
Bu, çok fazla gereksiz ek yük gibi görünüyor. Bu tür bir işleme gerçekten ihtiyaç duyanlar muhtemelen yüksek performansa ihtiyaç duyuyorlar çünkü akışlardan / soketlerden yazıyorlar / okuyorlar, bu durumda, önbelleğe alma values()(enum değerleriniz sıralıysa) veya basit bir switchifade bu yöntemi kolayca yenebilir. . Yalnızca bir avuç girişiniz varsa Enum, bir HashMap'in ek yükünü sadece switchifadeyi güncellemenin rahatlığı için eklemek pek mantıklı olmaz . Bu yöntem daha zarif görünebilir, ancak aynı zamanda israftır.
ezmek

30

Orada statik bir yöntem olduğunu edilir belgelenmiş, ama bunu beklediğiniz değil burada: 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];}
}

Prensipte sadece kullanabilirsiniz values()[i], ancak values()her çağrıldığında dizinin bir kopyasını oluşturacak söylentiler vardır .


9
Joshua Bloch'a (Etkili Java Kitabı) göre : Bir enum ile ilişkili bir değeri asla ordinalinden türetmeyin; Uygulamanız, numaralandırma sırasına bağlı olmamalıdır.
stevo.mit

4
Neyin uygulanması ? Bazı algoritma uygularsanız, uygulama çeteleler sipariş güvenmemelisiniz sürece bu düzen belgelenmiştir. Numaralandırmanın kendisini gerçekleştirdiğimizde, bu tür uygulama ayrıntılarını kullanmakta olduğu gibi, sınıf-özel yöntemleri kullanmakta sorun yoktur.
18446744073709551615

1
Kabul etmeyin. Belgelere bakmaksızın asla kastedilmediğine inanıyorum . Numaralamayı kendiniz uygulasanız bile sıra sayılarını kullanmamalısınız. Bu kötü bir koku ve hataya meyillidir. Uzman değilim ama Joshua Bloch ile tartışmam :)
stevo.mit

4
@ stevo.mit Java 8'deki yeni enum java.time.Month'a bir göz atın. Month.of (int) statik yöntemi, Joshua Bloch'un "asla" yapmamanız gerektiğini söylediği şeyi yapar. Sırasına göre bir Ay döndürür.
Klitos Kyriacou

1
@ stevo.mit Sıralı numaralandırmalar ve sırasız numaralandırmalar var . (Ve bit maskesi numaralandırmaları da.) Onlardan sadece "numaralandırma" olarak bahsetmek yanlıştır. Hangi ifade araçlarının kullanılacağına dair karar, üzerinde çalıştığınız soyutlama seviyesine dayanmalıdır . Uygulama ayrıntılarını (alt düzeyden ifade edici araçlar) veya kullanım varsayımlarını (daha yüksek düzeyden ifade edici araçlar) kullanmak gerçekten yanlıştır. " Hiçbir zaman " a gelince , insan dillerinde asla asla anlamına gelmez, çünkü her zaman bir bağlam vardır. (Genellikle, uygulama programlamasında, asla ...) BTW, programering.com/a/MzNxQjMwATM.html
18446744073709551615

16

PcapLinkType.values ​​() 'i yinelediğiniz ve aşağıdakileri karşılaştırdığınız yeni bir statik yöntem oluşturmanız gerekecektir:

public static PcapLinkType forCode(int code) {
    for (PcapLinkType typе : PcapLinkType.values()) {
        if (type.getValue() == code) {
            return type;
        }
    }
    return null;
 }

Nadiren çağrılırsa bu iyi olur. Sık sık çağrılıyorsa, Mapbaşkaları tarafından önerilen optimizasyona bakın .


4
Çok aranırsa pahalı olabilir. Statik bir harita oluşturmanın daha iyi amorti edilmiş maliyet vermesi muhtemeldir.
dty

@dty o (n) n = 200 ile - bunun bir sorun olduğunu düşünmüyorum
Bozho

7
Bu, ne sıklıkla çağrıldığına dair bir his olmadan tamamen saçma bir ifade. Bir kez aranırsa, tamam. Bir 10Ge ağında geçen her paket için çağrılırsa, bir algoritmayı 200 kat daha hızlı yapmak çok önemlidir. Bu yüzden
ifademi

10

Bunların hepsini otomatik olarak bir koleksiyona kaydetmek için buna benzer bir şey yapabilirsiniz ve bununla tam sayıları karşılık gelen numaraya kolayca dönüştürebilirsiniz. (BTW, bunları enum yapıcısında haritaya eklemeye izin verilmez . Java'yı yıllarca kullandıktan sonra bile yeni şeyler öğrenmek güzel. :)

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
Göndermeden önce cevabınızı iki kez kontrol ettiğinizde bu elde edersiniz. ;)
Esko Luontola

10

eğer böyle bir numaraya sahipsen

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;
    }
}

o zaman onu beğenebilirsin

PcapLinkType type = PcapLinkType.values()[1]; /*convert val to a PcapLinkType */

yorumu kaçırdınız / * Snippet, 200 numaralandırma daha, her zaman ardışık değil. * /
MeBigFatGuy

sadece sıralama değerinizin Sıfırdan geçişlilik olması durumunda, bu kötü bir uygulamadır
cuasodayleo

4

@MeBigFatGuy'un dediği gibi, bloğunuzun koleksiyon static {...}üzerinde bir döngü kullanmasını sağlayabilmeniz dışında values():

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

4

Bu sorunun birkaç yıllık olduğunu biliyorum, ancak bu arada Java 8 bize getirdiği için Optional, onu kullanarak bir çözüm sunabileceğimi düşündüm ( Streamve Collectors):

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şuna benzer null: (geçerli) değer olmadığında bir durumu temsil eder. Ancak bu, veya vakalarını kontrol etmeyi unutabileceğiniz için daha güvenli bir alternatif nullveya varsayılan bir değerdir . İkisi de geçerli değerlerdir! Bunun aksine, bir tür değişkenine bir değer atayamazsınız . önce geçerli bir değeri kontrol etmenizi sağlar.DLT_UNKNOWNnullDLT_UNKNOWNPcapLinkTypeOptional<PcapLinkType>PcapLinkTypeOptional

Elbette, DLT_UNKNOWNgeriye dönük uyumluluk veya başka bir nedenle korumak istiyorsanız, Optionalbu durumda bile, orElse()bunu varsayılan değer olarak belirtmek için kullanabilirsiniz:

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

Numaranıza intbir parametre olarak kabul eden ve bir PcapLinkType.

public static PcapLinkType of(int linkType) {

    switch (linkType) {
        case -1: return DLT_UNKNOWN
        case 0: return DLT_NULL;

        //ETC....

        default: return null;

    }
}

switchYeni bir enum eklerseniz , bu ifadeye bir girdi eklemeyi unutmasanız iyi olur. İdeal değil, IMHO.
dty

1
@dty Yani, HashMap'in ek yükünün, switch ifadesine yeni bir servis talebi ekleme ihtiyacından daha ağır bastığını mı düşünüyorsunuz?
ezmek

1
Sanırım hata yapmama yardımcı olan ve bu nedenle bir hash aramasının mikro performansına odaklanmadan önce doğru olma olasılığı daha yüksek olan bir kod yazmayı tercih ederim.
dty

3

Kullandığım şey bu:

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); }
                    }

Sıralı kullanmak kötü uygulama olarak tanımlanmıştır, genel olarak kaçınmak daha iyidir.
Rafael

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
Çok arandığında pahalı. Diziyi güncellemeyi hatırlamanız gerekir (numaralandırmalar bir .values()yöntemi tanımlarken neden ona sahipsiniz ?).
dty

@dty, dene / yakala mı? Değerlerin çoğu DLT_UNKNOWN kategorisine girerse pahalı olduğunu söylemenin daha adil olacağını düşünüyorum.
nsfyn55

1
Bir dizi çözümünün reddedildiğini ve bir harita çözümünün oylandığını görmek beni gerçekten şaşırttı. Burada hoşlanmadığım şey --int, ama kesinlikle bir yazım hatası.
18446744073709551615

Anlıyorum: istedikleri nullyerine DLT_UKNOWN:)
18446744073709551615

1
Neden olmasın static final values[] = PcapLinkType.values()?
18446744073709551615

0

Tamsayı tabanlı numaralandırılmış türleri zarif bir şekilde ele almanın bir yolu yoktur. Çözümünüz yerine dize tabanlı bir numaralandırma kullanmayı düşünebilirsiniz. Her zaman tercih edilen bir yol değil, ama hala var.

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);
  }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.