Konvertieren Sie einen ganzzahligen Wert in eine übereinstimmende Java-Aufzählung

85

Ich habe eine Aufzählung wie diese:

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

Jetzt bekomme ich ein Int von der externen Eingabe und möchte die passende Eingabe - eine Ausnahme auszulösen, wenn ein Wert nicht existiert, ist in Ordnung, aber vorzugsweise hätte ich es DLT_UNKNOWN in diesem Fall.

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

Antworten:

104

Sie müssten dies manuell tun, indem Sie der Klasse eine statische Zuordnung hinzufügen, die Ganzzahlen Aufzählungen zuordnet, z

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;
}
MeBigFatGuy
quelle
1
aktualisiert mit Empfehlungen von dty, was eine gute Idee war.
MeBigFatGuy
Ich hoffe, Sie haben meinen Code zuerst durch einen Compiler laufen lassen ... Ich habe es mir gerade aus dem Kopf gemacht. Ich weiß, dass die Technik funktioniert - ich habe sie gestern benutzt. Aber der Code befindet sich auf einem anderen Computer und dieser hat nicht meine Entwicklungswerkzeuge.
dty
1
allOf ist nur für Sets verfügbar
MeBigFatGuy
1
Außerdem EnumMapverwendet die Aufzählungen als Schlüssel. In diesem Fall möchte das OP die Aufzählungen als Werte.
Tag
8
Dies scheint eine Menge unnötiger Gemeinkosten zu sein. Diejenigen, die diese Art von Operation tatsächlich benötigen, benötigen wahrscheinlich eine hohe Leistung, da sie aus Streams / Sockets schreiben / lesen. In diesem Fall würde das Caching von values()(wenn Ihre Aufzählungswerte sequentiell sind) oder eine einfache switchAnweisung diese Methode leicht übertreffen . Wenn Sie nur eine Handvoll Einträge in Ihrem EnumVerzeichnis haben, ist es wenig sinnvoll, den Overhead einer HashMap hinzuzufügen, nur um die switchAnweisung nicht aktualisieren zu müssen . Diese Methode mag eleganter erscheinen, ist aber auch verschwenderisch.
Crush
30

Es gibt eine statische Methode , values()die ist dokumentiert, aber nicht , wo man es erwarten würde: http://docs.oracle.com/javase/tutorial/java/javaOO/enum.html

enum MyEnum {
    FIRST, SECOND, THIRD;
    private static MyEnum[] allValues = values();
    public static MyEnum fromOrdinal(int n) {return allValues[n];}
}

Im Prinzip können Sie nur verwenden values()[i], aber es gibt Gerüchte, dass values()bei jedem Aufruf eine Kopie des Arrays erstellt wird.

18446744073709551615
quelle
9
Laut Joshua Bloch (Effective Java Book) : Leiten Sie niemals einen Wert, der einer Aufzählung zugeordnet ist, von ihrer Ordnungszahl ab; Ihre Implementierung sollte sich nicht auf die Reihenfolge der Aufzählungen stützen.
stevo.mit
4
Umsetzung von was? Wenn wir einen Algorithmus implementieren, sollte sich die Implementierung nicht auf die Reihenfolge der Aufzählungen stützen, es sei denn , diese Reihenfolge ist dokumentiert. Wenn wir die Aufzählung selbst implementieren, ist es in Ordnung, solche Implementierungsdetails genauso zu verwenden wie klassenprivate Methoden.
18446744073709551615
1
Stimme nicht zu. Ich glaube nie ist unabhängig von Dokumentation gemeint. Sie sollten keine Ordnungszahlen verwenden, auch wenn Sie enum selbst implementieren. Es ist ein schlechter Geruch und fehleranfällig. Ich bin kein Experte, aber ich würde nicht mit Joshua Bloch streiten :)
stevo.mit
4
@ stevo.mit werfen Sie einen Blick auf die neue Enumeration java.time.Month in Java 8. Die statische Methode Month.of (int) macht genau das, was Joshua Bloch gesagt hat, Sie sollten "nie" tun. Es gibt einen Monat basierend auf seiner Ordnungszahl zurück.
Klitos Kyriacou
1
@ stevo.mit Es gibt geordnete und ungeordnete Aufzählungen . (Und Bitmasken-Aufzählungen auch.) Es ist einfach falsch, sie nur als "Aufzählungen" zu bezeichnen. Die Entscheidung, was Ausdruck bedeutet, muss auf der Abstraktionsebene basieren, an der Sie arbeiten. Es ist in der Tat falsch, Implementierungsdetails (Ausdrucksmittel von der unteren Ebene) oder Verwendungsannahmen (Ausdrucksmittel von der höheren Ebene) zu verwenden. Was " nie " betrifft, bedeutet in menschlichen Sprachen niemals niemals nie, weil es immer einen Kontext gibt. ( In der Regel, in der Anwendungsprogrammierung nie ...) BTW, programering.com/a/MzNxQjMwATM.html
18446744073709551615
13

Sie müssen eine neue statische Methode erstellen, bei der Sie PcapLinkType.values ​​() iterieren und vergleichen:

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

Das wäre in Ordnung, wenn es selten genannt wird. Wenn es häufig aufgerufen wird, sehen Sie sich die Mapvon anderen vorgeschlagene Optimierung an.

Bozho
quelle
4
Könnte teuer sein, wenn man viel ruft. Das Erstellen einer statischen Karte führt wahrscheinlich zu besseren Amortisationskosten.
dty
@dty o (n) mit n = 200 - ich glaube nicht, dass es ein Problem ist
Bozho
7
Das ist eine total lächerliche Aussage ohne ein Gefühl dafür, wie oft sie aufgerufen wird. Wenn es einmal aufgerufen wird, gut. Wenn es für jedes Paket aufgerufen wird, das in einem 10Ge-Netzwerk vorbeizieht, ist es sehr wichtig, einen Algorithmus 200-mal schneller zu machen. Daher qualifizierte ich meine Aussage mit "wenn viel gerufen"
Tag
10

Sie können so etwas tun, um sie alle automatisch in einer Sammlung zu registrieren, mit der Sie dann die Ganzzahlen einfach in die entsprechende Aufzählung konvertieren können. (Übrigens ist es nicht erlaubt , sie der Karte im Enum-Konstruktor hinzuzufügen . Es ist schön, auch nach vielen Jahren mit Java neue Dinge zu lernen. :)

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);
    }
}
Esko Luontola
quelle
1
Das bekommen Sie, wenn Sie Ihre Antwort vor dem Posten noch einmal überprüfen. ;)
Esko Luontola
10

wenn du so eine Aufzählung hast

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

dann kannst du es gerne benutzen

PcapLinkType type = PcapLinkType.values()[1]; /*convert val to a PcapLinkType */
Jack Gajanan
quelle
Sie haben den Kommentar verpasst / * snip, 200 weitere Aufzählungen, nicht immer aufeinanderfolgend. * /
MeBigFatGuy
Nur für den Fall, dass Ihr Enum-Wert die Transitivität von Null ist, ist dies eine schlechte Praxis
cuasodayleo
4

Wie @MeBigFatGuy sagt, außer dass Sie Ihren static {...}Block dazu bringen können, eine Schleife über die values()Sammlung zu verwenden:

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

Ich weiß, dass diese Frage ein paar Jahre alt ist, aber da Java 8 uns inzwischen gebracht hat Optional, dachte ich, ich würde eine Lösung anbieten, die es (und Streamund Collectors) verwendet:

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

Optionalist wie null: Es stellt einen Fall dar, in dem es keinen (gültigen) Wert gibt. Es ist jedoch eine typsichere Alternative zu nulloder ein Standardwert, z. B. DLT_UNKNOWNweil Sie vergessen könnten, nach nulloder zu DLT_UNKNOWNsuchen. Sie sind beide gültige PcapLinkTypeWerte! Im Gegensatz dazu können Sie Optional<PcapLinkType>einer Variablen vom Typ keinen Wert zuweisen PcapLinkType. OptionalLässt Sie zuerst nach einem gültigen Wert suchen.

Wenn Sie aus DLT_UNKNOWNGründen der Abwärtskompatibilität oder aus einem anderen Grund beibehalten möchten, können Sie dies natürlich Optionalauch in diesem Fall verwenden, indem Sie orElse()es als Standardwert angeben:

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);
  }
}
Brad Collins
quelle
3

Sie können Ihrer Aufzählung eine statische Methode hinzufügen, die a intals Parameter akzeptiert und a zurückgibt PcapLinkType.

public static PcapLinkType of(int linkType) {

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

        //ETC....

        default: return null;

    }
}
Buhake Sindi
quelle
Vergessen Sie besser nicht, dieser switchAnweisung einen Eintrag hinzuzufügen, wenn Sie eine neue Aufzählung hinzufügen. Nicht ideal, IMHO.
dty
1
@dty Sie denken also, dass der Overhead einer HashMap die Notwendigkeit überwiegt, einer switch-Anweisung einen neuen Fall hinzuzufügen?
Crush
1
Ich denke, ich würde lieber Code schreiben, der mir hilft, keine Fehler zu machen, und daher eher korrekt ist, bevor ich mich auf die Mikroleistung einer Hash-Suche konzentriere.
dty
3

Das benutze ich:

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); }
                    }
18446744073709551615
quelle
Die Verwendung von Ordnungszahlen wurde als schlechte Praxis identifiziert. Im Allgemeinen ist es besser, sie zu vermeiden.
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;    
    }    
}    
nsfyn55
quelle
1
Teuer, wenn man viel nennt. Denken Sie daran, das Array zu aktualisieren (warum haben Sie es überhaupt, wenn Enums eine .values()Methode definieren ?).
dty
@dty ist es der Versuch / Fang? Ich denke, es wäre fairer zu sagen, dass es teuer ist, wenn viele der Werte in die Kategorie DLT_UNKNOWN fallen.
nsfyn55
1
Ich bin wirklich überrascht zu sehen, dass eine Array-Lösung abgelehnt und eine Kartenlösung abgelehnt wurde. Was ich hier nicht mag, ist --int, aber es ist offensichtlich ein Tippfehler.
18446744073709551615
Ich sehe: sie wollen nullanstelle von DLT_UKNOWN:)
18446744073709551615
1
Warum nicht static final values[] = PcapLinkType.values()?
18446744073709551615
0

Es gibt keine Möglichkeit, ganzzahlige Aufzählungstypen elegant zu behandeln. Möglicherweise möchten Sie anstelle Ihrer Lösung eine auf Zeichenfolgen basierende Aufzählung verwenden. Nicht immer ein bevorzugter Weg, aber er existiert immer noch.

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