Cast Int in Java aufzählen

331

Was ist der richtige Weg, um ein Int in eine Aufzählung in Java umzuwandeln, wenn die folgende Aufzählung vorliegt?

public enum MyEnum
{
    EnumValue1,
    EnumValue2
}


MyEnum enumValue = (MyEnum) x; //Doesn't work???
Maxim Gershkovich
quelle
Beantwortet das deine Frage? Enum mit int value verknüpfen
Ryu S. vor

Antworten:

583

Versuchen Sie, MyEnum.values()[x]wo sein xmuss 0oder 1dh eine gültige Ordnungszahl für diese Aufzählung.

Beachten Sie, dass Aufzählungen in Java tatsächlich Klassen sind (und Aufzählungswerte somit Objekte sind) und Sie daher keine intoder sogar Integerkeine Aufzählung umwandeln können.

Thomas
quelle
115
+1: Möglicherweise möchten Sie zwischenspeichern, MyEnum.values()da dies teuer ist. dh wenn Sie es hunderte Male nennen.
Peter Lawrey
6
@ PeterLawrey Kannst du der Vollständigkeit halber erklären, warum es langsam sein sollte? Ich sehe keinen offensichtlichen Grund dafür.
Tarrasch
48
@Tarrasch Da Arrays veränderbar sind, muss values ​​() eine Kopie des Arrays von Elementen zurückgeben, nur für den Fall, dass Sie es ändern. Das Erstellen dieser Kopie ist jedes Mal relativ teuer.
Peter Lawrey
9
@ PeterLawrey Ich habe in letzter Zeit zu viel Haskell verwendet (wo alles unveränderlich ist)! Vielen Dank für Ihre klare und präzise Erklärung. :)
Tarrasch
Wie bekomme ich das 'x'?
Beeing Jk
160

MyEnum.values()[x]ist eine teure Operation. Wenn die Leistung ein Problem darstellt, möchten Sie möglicherweise Folgendes tun:

public enum MyEnum {
    EnumValue1,
    EnumValue2;

    public static MyEnum fromInteger(int x) {
        switch(x) {
        case 0:
            return EnumValue1;
        case 1:
            return EnumValue2;
        }
        return null;
    }
}
Lorenzo Polidori
quelle
44
Wenn Sie die Switch-Wartung vermeiden möchten, verwenden Sie die using-Klasse: private final MyEnum [] myEnumValues ​​= MyEnum.values ​​(); Dann Verwendung: myEnum = myEnumValues ​​[i];
Gili Nachum
23
@GiliNachum sagte es auf seltsame Weise, aber das Problem mit dieser Lösung ist die Wartbarkeit. Dies verstößt gegen das DRY-Prinzip, dh, wenn die Aufzählungswerte geändert werden (neu angeordnet, Wert (e) hinzugefügt, Wert (e) entfernt), muss die switch-Anweisung gleichzeitig aktualisiert werden. Gilis Kommentar zwingt Java, die Konsistenz mit der Liste der Aufzählungswerte aufrechtzuerhalten. Änderungen an den Aufzählungswerten wirken sich überhaupt nicht auf diesen Ansatz aus.
Dandre Allison
3
@ LorenzoPolidori, können Sie erklären, warum Sie MyEnum.values()[x]eine teure Operation betrachten. Ich weiß nicht, wie es im Detail funktioniert, aber für mich scheint es keine große Sache zu sein, auf ein Element in einem Array zuzugreifen, auch bekannt als konstante Zeit. Wenn das Array erstellt werden muss, dauert es O (n), was der Laufzeit Ihrer Lösung entspricht.
Brunsgaard
1
@brunsgaard Ich gehe davon values()aus, dass jedes Mal ein neues Array generiert wird, da Arrays veränderbar sind und es nicht sicher ist, dasselbe mehrmals zurückzugeben. Switch-Anweisungen sind nicht unbedingt O (n), sie können zu Sprungtabellen kompiliert werden. Lorenzos Behauptungen scheinen also berechtigt zu sein.
MikeFHay
1
Die Version von @Johnson Gili erfordert keine zusätzlichen Codeänderungen außerhalb der Änderungen in der Enum-Deklaration. Wenn Sie der Aufzählung ein neues Element hinzufügen, myEnum = myEnumValues[i]wird das idritte Element in der Aufzählung ohne Änderungen zurückgegeben.
Dandre Allison
45

Wenn Sie Ihre ganzzahligen Werte angeben möchten, können Sie eine Struktur wie die folgende verwenden

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;
        }
}
Arzt
quelle
5
+1, weil es die Tatsache hervorhebt, dass Werte nicht aufeinanderfolgend sein müssen.
Rhavin
Dies scheint auch die einzige Antwort zu sein, die funktioniert, wenn Sie einen spärlichen (oder wiederholten) Satz ganzzahliger Werte wünschen, anstatt die Standard-Ordnungszahlen von 0 .. (count-1) zu verwenden. Dies kann wichtig sein, wenn Sie mit vorhandenem Code interagieren, z. B. über ein Netzwerk.
Benkc
Es sei darauf hingewiesen, dass es sich wie in den obigen Antworten wahrscheinlich lohnt, das Ergebnis von values ​​() zwischenzuspeichern, um bei jedem Aufruf eine Speicherzuweisung und Arraykopie zu vermeiden.
Benkc
1
Abhängig von der Länge Ihrer Aufzählung möchten Sie möglicherweise eine HashMap erstellen oder eine binäre Suche oder etwas anderes für diese Suche verwenden, anstatt jedes Mal eine lineare Suche durchzuführen.
Benkc
2
Dies sollte der richtige Weg und die beste Vorgehensweise sein, um int in enum zu konvertieren, und ich denke, Sie können das Problem durch öffentliche statische A GetValue (int _id) {für (A a: A.values ​​() {if (a.getId () vereinfachen ) == _ id) {return a;}} return null;} Werde das None los, isEmpty () und vergleiche () Zeug.
Chris.Zou
35

Sie können es so versuchen.
Klasse mit Element-ID erstellen.

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

Holen Sie sich jetzt diese Aufzählung mit id als int.

MyEnum myEnum = MyEnum.fromId(5);
Piyush Ghediya
quelle
19

Ich speichere die Werte zwischen und erstelle eine einfache statische Zugriffsmethode:

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];
    }
}
Ossys
quelle
4
Dies ist eine Lösung, die ich jetzt benutze. Aber meiner Meinung nach ist es weniger verwirrend, wenn Sie dem Feld nicht valuesden gleichen Namen wie der Methode geben values(). Ich benutze cachedValuesfür Feldnamen.
ToolmakerSteve
2
Effizient und elegant; kopiert und in mein Projekt eingefügt :) Das einzige, was ich geändert habe, ist fromInt (int i), das ich gerade von (int i) aufgerufen habe, weil es etwas redundant ist, int zweimal in der Signatur zu haben.
Pipedreambomb
1
Warum nicht von Anfang an initialisieren? Warum auf den ersten Treffer warten?
Kaiser
9

Java-Enums haben nicht die gleiche Art von Enum-to-Int-Zuordnung wie in C ++.

Das heißt, alle Aufzählungen haben eine valuesMethode, die ein Array möglicher Aufzählungswerte zurückgibt

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

sollte arbeiten. Es ist ein bisschen böse und es ist vielleicht besser, wenn möglich nicht zu versuchen, von ints nach Enums (oder umgekehrt) zu konvertieren .

Cameron Skinner
quelle
9

Dies ist normalerweise nicht der Fall, daher würde ich es mir noch einmal überlegen. Allerdings sind die grundlegenden Operationen: int -> enum using EnumType.values ​​() [intNum] und enum -> int using enumInst.ordinal ().

Da jede Implementierung von values ​​() keine andere Wahl hat, als Ihnen eine Kopie des Arrays zu geben (Java-Arrays sind niemals schreibgeschützt), sollten Sie eine EnumMap verwenden, um die Zuordnung enum -> int zwischenzuspeichern.

Dilum Ranatunga
quelle
2
Betreff "Dies ist normalerweise nicht der Fall": Häufiger Fall, in dem dies nützlich ist: enum entspricht int-Werten, die in einer Datenbank gespeichert sind.
ToolmakerSteve
@ToolmakerSteve Sie haben absolut Recht, dass die Zuordnungen erforderlich sind. Aber möchten Sie diese Art der Codierung einem OR-Mapper oder einem Toolkit / einer Bibliothek überlassen?
Dilum Ranatunga
7

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

w.donahue
quelle
6

Hier ist die Lösung, mit der ich gehen möchte. Dies funktioniert nicht nur mit nicht sequentiellen Ganzzahlen, sondern sollte auch mit jedem anderen Datentyp funktionieren, den Sie möglicherweise als zugrunde liegende ID für Ihre Aufzählungswerte verwenden möchten.

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

Ich muss IDs nur zu bestimmten Zeiten (beim Laden von Daten aus einer Datei) in Aufzählungen konvertieren, daher gibt es für mich keinen Grund, die Karte jederzeit im Speicher zu behalten. Wenn Sie möchten, dass auf die Karte jederzeit zugegriffen werden kann, können Sie sie jederzeit als statisches Mitglied Ihrer Enum-Klasse zwischenspeichern.

Jkindwall
quelle
IMHO, wenn ich über die Speichernutzung besorgt wäre, würde ich die HashMap dynamisch erstellen - wie @ossys, aber mit unterschiedlichem Code, wenn der Cache null ist, und dann eine zweite Methode hinzufügen, clearCachedValueswenn Sie damit fertig sind (wodurch das private Feld wieder auf null gesetzt wird). Ich halte es für MyEnum.fromInt(i)einfacher zu verstehen, als ein Kartenobjekt herumzugeben.
ToolmakerSteve
6

Falls es anderen hilft, verwendet die von mir bevorzugte Option, die hier nicht aufgeführt ist, die Kartenfunktion von 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);
    }
}

Mit der Standardeinstellung, die Sie verwenden können null, können throw IllegalArgumentExceptionoder können Sie fromIntein Optionalbeliebiges Verhalten zurückgeben.

Chad Befus
quelle
3
Sie sollten erwähnen, dass Sie Guava verwenden. Oder Sie können Streams verwenden:Map<Integer, MyEnum> reverseLookup = Arrays.stream(MyEnum.values()).collect(Collectors.toMap(MyEnum::getValue, Function.identity()));
Shmosel
1
Vielleicht möchten Sie auch eine getValue()Methode definieren .
Shmosel
@shmosel Ups, ich habe die Funktion getValue verpasst, danke. Das Eingeben generischer Versionen in den Stackoverflow wird nicht immer ausgeführt. Kommentar zur Verwendung von Guava mit einem Link hinzugefügt. Ziehen Sie die Guava-Methode Streams vor.
Chad Befus
3

Sie können die values()Aufzählung durchlaufen und den ganzzahligen Wert der Aufzählung mit dem folgenden Wert vergleichen 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
    }
}

Und benutze einfach so:
TestEnum x = TestEnum.getEnum(4);//Will return TestEnum.Value4
Ich hoffe das hilft;)

Yashar Aliabbasi
quelle
3

Basierend auf der Antwort von @ChadBefus und dem Kommentar von @shmosel würde ich empfehlen, dies zu verwenden. (Effiziente Suche und funktioniert mit reinem 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());
    }
}
Yuki Inoue
quelle
0

Eine gute Option ist es, die Konvertierung von intnach zu vermeidenenum : Wenn Sie beispielsweise den Maximalwert benötigen, können Sie x.ordinal () mit y.ordinal () vergleichen und x oder y entsprechend zurückgeben. (Möglicherweise müssen Sie Ihre Werte neu anordnen, um einen solchen Vergleich aussagekräftig zu machen.)

Wenn das nicht möglich ist, würde ich MyEnum.values()in einem statischen Array speichern .

18446744073709551615
quelle
1
Wenn Sie int von DB erhalten und es in Enum konvertieren möchten, was meiner Meinung nach eine sehr häufige Aufgabe ist, benötigen Sie int -> enum-Konvertierung, IMO ..
Yuki Inoue
0

Dies ist die gleiche Antwort wie bei den Ärzten, zeigt jedoch, wie das Problem mit veränderlichen Arrays behoben werden kann. Wenn Sie diese Art von Ansatz aufgrund der Verzweigungsvorhersage zuerst verwenden, hat dies nur einen sehr geringen bis Null-Effekt und der gesamte Code ruft nur einmal veränderbare Array-Werte () auf. Da beide Variablen statisch sind, verbrauchen sie nicht bei jeder Verwendung dieser Aufzählung n * Speicher.

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;
}
Ali Akdurak
quelle
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;
        }
    }
}
Zoso
quelle
@shmosel Ich glaube, Ausnahmen sind deutlich besser, wenn die Grenzen relativ gering sind.
Zoso
Worauf basiert Ihre Überzeugung?
Shmosel
0

In Kotlin:

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

Verwendungszweck:

val status = Status.getStatus(1)!!
CoolMind
quelle
0

Schrieb diese Implementierung. Es ermöglicht fehlende und negative Werte und hält den Code konsistent. Die Karte wird ebenfalls zwischengespeichert. Verwendet eine Schnittstelle und benötigt Java 8.

Aufzählung

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

Schnittstelle

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;
    }
}
Gerrit Brink
quelle