Enum mit int value verknüpfen

88

Zuvor hatte ich meine LegNo-Aufzählungen einfach wie folgt definiert:

NO_LEG, LEG_ONE, LEG_TWO

und durch einen Anruf return LegNo.values()[i];konnte ich den Wert erhalten, der jeder Aufzählung zugeordnet ist.

Aber jetzt habe ich beschlossen, dass die LegNoAufzählung NO_LEGint -1 statt 0 sein soll, also habe ich beschlossen, einen privaten Konstruktor zu verwenden, um seinen int-Wert zu initialisieren und festzulegen

NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

private LegNo(final int leg) { legNo = leg; }

Das einzige, was jetzt ist, ist, dass die values()Methode für die NO_LEGAufzählung nicht funktioniert , weil ich es so mache . Wie erhalte ich die mit dem int verknüpfte Aufzählung? Gibt es eine effiziente Möglichkeit, dies zu tun, außer eine case switch-Anweisung oder ein if-elseif-elseif zu verwenden?

Ich kann viele SO-Fragen sehen, die damit zu tun haben, den int-Wert aus der Aufzählung zu erhalten, aber ich bin hinter dem Gegenteil her.

L-Samuels
quelle

Antworten:

144

EDIT August 2018

Heute würde ich dies wie folgt umsetzen

public enum LegNo {
    NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

    private final int value;

    LegNo(int value) {
        this.value = value;
    }

    public static Optional<LegNo> valueOf(int value) {
        return Arrays.stream(values())
            .filter(legNo -> legNo.value == value)
            .findFirst();
    }
}

Sie müssen eine Zuordnung innerhalb der Aufzählung pflegen.

public enum LegNo {
    NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

    private int legNo;

    private static Map<Integer, LegNo> map = new HashMap<Integer, LegNo>();

    static {
        for (LegNo legEnum : LegNo.values()) {
            map.put(legEnum.legNo, legEnum);
        }
    }

    private LegNo(final int leg) { legNo = leg; }

    public static LegNo valueOf(int legNo) {
        return map.get(legNo);
    }
}

Der statische Block wird nur einmal aufgerufen, sodass hier praktisch keine Leistungsprobleme auftreten.

BEARBEITEN: Die Methode wurde in umbenannt, valueOfda sie eher mit anderen Java-Klassen übereinstimmt.

adarshr
quelle
Entschuldigung, ich bin mir nicht sicher, ob ich klar genug war. Ich möchte das int übergeben und die damit verbundene Aufzählung erhalten.
L-Samuels
@ L-Samuels Vermutlich habe ich deine Frage nicht richtig gelesen. Siehe mein Update.
Adarshr
2
Ich weiß, das scheint offensichtlich, aber benutze es so : LegNo foo = LegNo.valueOf(2);. Der vorherige Code gibt a zurück LegNo.LEG_TWO.
FirstOne
1
Zu beachten ist, dass die Übergabe eines ungültigen ganzzahligen Werts (nicht zugeordnet) nullwie erwartet mithilfe von HashMap.get zurückgegeben wird : Gibt den Wert zurück, dem der angegebene Schlüssel zugeordnet ist, oder null, wenn diese Zuordnung keine Zuordnung für den Schlüssel enthält.
FirstOne
Obwohl die Stream-Syntax ordentlich ist, sollte darauf hingewiesen werden, dass sie eine höhere zeitliche Komplexität aufweist als die statische Zuordnung (die zugegebenermaßen einen höheren Speicherverbrauch aufweist). Kein Problem für 3 Werte, aber definitiv ein Problem, wenn Sie valueOf()eine 1000-Mitglieder-Enumeration in einer anderen Schleife verwenden.
Patrick M
24

Sie können auch eine statische Methode in die Aufzählung aufnehmen, die alle Elemente durchläuft und die richtige zurückgibt.

public enum LegNo {
   NO_LEG(-1),
   LEG_ONE(1),
   LEG_TWO(2);

   private int legIndex;

   private LegNo(int legIndex) { this.legIndex = legIndex; }

   public static LegNo getLeg(int legIndex) {
      for (LegNo l : LegNo.values()) {
          if (l.legIndex == legIndex) return l;
      }
      throw new IllegalArgumentException("Leg not found. Amputated?");
   }
}

Wenn Sie nun einen Enum-Wert durch die Ganzzahl erhalten möchten, verwenden Sie einfach:

int myLegIndex = 1; //expected : LEG_ONE
LegNo myLeg = LegNo.getLeg(myLegIndex);
Mike Adler
quelle
Ich nehme an, dies wäre eleganter als die Verwendung einer if else if-Anweisung. Angesichts der Tatsache, dass mehr Nachforschungen erforderlich waren, wäre die von @adarshr vorgeschlagene Kartenstrategie besser. Obwohl für den Humor stimmen.
L-Samuels
1
Ich mag die Kartenstrategie auch sehr. Besonders wenn die Aufzählung entweder viele Werte hat oder sehr oft über diesen Mechanismus nachgeschlagen werden muss. Wenn das Nachschlagen von Werten durch das zugehörige int jedoch relativ selten vorkommt oder Sie viele verschiedene Aufzählungen mit derselben Suchanforderung haben, ist mein Weg meiner Meinung nach ressourcenschonender, da der Overhead für die Karte gespart wird. Außerdem finde ich, dass der Code weniger überladen ist. Ich habe jedoch einige Anwendungsfälle, in denen ich definitiv selbst zum Map-Typ wechseln werde.
Mike Adler
Sie sollten niemals einen zugeordneten Aufzählungswert anhand seiner Ordnungszahl ableiten. Die Verwendung einer statischen Karte ist die von Java-Architekten empfohlene Methode.
Hfontanez
Das legIndex-Feld stimmt mit der Ordnungszahl in diesem Beispiel überein, kann jedoch ein beliebiger int-Wert sein. Es wird keine ordinale Suche durchgeführt. Bitte geben Sie auch einen Grund an oder verlinken Sie ihn, warum Sie der Meinung sind, dass die ordinale Suche schlecht ist.
Mike Adler
1
"Bein nicht gefunden. Amputiert?"
Gnagy
17

adarshrs Antwort an Java 8 angepasst:

import static java.util.Arrays.stream;
import static java.util.stream.Collectors.toMap;

import java.util.Map;

public enum LegNo {
    NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

    private final int legNo;

    private final static Map<Integer, LegNo> map =
            stream(LegNo.values()).collect(toMap(leg -> leg.legNo, leg -> leg));

    private LegNo(final int leg) {
        legNo = leg;
    }

    public static LegNo valueOf(int legNo) {
        return map.get(legNo);
    }
}
Marcin
quelle
11

Sie können auch auf den Enum-Wert zugreifen, der dem angegebenen Integer-Wert entspricht, indem Sie einfach die Methode values ​​() in enum LegNo aufrufen. Es gibt ein Feld mit LegNo-Aufzählungen zurück: LegNo.values()[0]; //returns LEG_NO LegNo.values()[1]; //returns LEG_ONE LegNo.values()[2]; //returns LEG_TWO

Nicht genau das, wonach er suchte, aber ziemlich nah und in der Tat sehr einfach. (Obwohl das Thema tot ist, kann es für jemand anderen nützlich sein.)

Tadeas
quelle
6

Java 8 Weg mit Standardwert:

public enum LegNo {
    NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

    private final int legNo;

    LegNo(int legNo) {
        this.legNo = legNo;
    }

    public static LegNo find(int legNo, Supplier<? extends LegNo> byDef) {
        return Arrays.asList(LegNo.values()).stream()
                .filter(e -> e.legNo == legNo).findFirst().orElseGet(byDef);
    }
}

anrufen:

LegNo res = LegNo.find(0, () -> LegNo.NO_LEG);

oder mit Ausnahme:

LegNo res = LegNo.find(0, () -> {
    throw new RuntimeException("No found");
});
Dmitry Sokolyuk
quelle
1

Da Ihre Aufzählung nur 3 Elemente enthält, besteht der schnellste Weg darin, nur eine Reihe von Elementen zu verwenden if else, wie Sie vorgeschlagen haben.

edit: Die Antwort, die adarshr gegeben hat, ist besser für allgemeine Fälle geeignet, in denen es viele Enum-Werte gibt, aber ich denke, es ist ein Overkill für Ihr Problem.

DieterDP
quelle
Ein Mapin Ihrem Code zu haben ist sicherlich kein Overkill. Außerdem macht es die Methode viel sauberer als ein Spaghetti von Wenn-Sonst-Bedingungen.
Adarshr
Ich bin damit einverstanden, dass die Map besser ist, wenn Sie viele Enum-Werte haben, aber für 3 Werte würde ich mich an ein if / else-Konstrukt halten. Es ist Geschmackssache, denke ich.
DieterDP
Unabhängig davon, welchen Ansatz wir wählen, sollte die Methodensignatur von public LegNo valueOf(int value)nicht geändert werden. Das if-else könnte dann innerhalb der Aufzählung selbst geschrieben werden. Wenn das if-else aus der Aufzählung herauskommt, wird es sicherlich ein nicht so sauberer Code.
Adarshr
1
Ich stimme dir vollkommen zu :)
DieterDP
1
public enum LegNo {
    NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

    private int legNo;

    private LegNo(int leg) { legNo = leg; }

    public static LegNo valueOf(int legNo) {
        for (LegNo leg : LegNo.values()) {
            if (leg.legNo == legNo) return leg;
        }   
    }
}

assert LegNo.valueOf(2) == LegNo.LEG_TWO
assert LegNo.valueOf(3) == null
Tom B.
quelle
4
Akzeptabel für Aufzählungen mit <10 Werten, aber für eine große Anzahl von Aufzählungswerten aufgrund der Komplexität der Suche völlig unwirksam. O (n)
Alfishe
1
public enum LegNo {

  NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

  private final int code;

  LegNo(int code) {
    this.code = code;
    ReverseStorage.reverseMap.put(code, this);
  }

  public static Optional<LegNo> getByCode(int code) {
    return Optional.ofNullable(ReverseStorage.reverseMap.get(code));
  }

  private static final class ReverseStorage {
    private static final Map<Integer, LegNo> reverseMap = new LinkedHashMap<>();
  }
}
Andrey Lebedenko
quelle