Konvertieren Sie von einer Ordnungszahl in einen Aufzählungstyp

315

Ich habe den Aufzählungstyp ReportTypeEnum, der zwischen Methoden in allen meinen Klassen übergeben wird, aber ich muss ihn dann an die URL übergeben, damit ich die ordinale Methode verwende, um den int-Wert zu erhalten. Nachdem ich es auf meiner anderen JSP-Seite erhalten habe, muss ich es wieder in eine konvertieren, ReportTypeEnumdamit ich es weiter übergeben kann.

Wie kann ich Ordnungszahlen in konvertieren ReportTypeEnum?

Verwenden von Java 6 SE.

Lennie
quelle
1
Bisher gibt es kein Java 6 EE (AFAIK). Es gibt Java SE 6 und Java EE 5.
Hosam Aly

Antworten:

632

Um eine Ordnungszahl in ihre Aufzählung umzuwandeln, möchten Sie möglicherweise Folgendes tun:

ReportTypeEnum value = ReportTypeEnum.values()[ordinal];

Bitte beachten Sie die Array-Grenzen.

Beachten Sie, dass jeder Aufruf von values()ein neu geklontes Array zurückgibt, was sich negativ auf die Leistung auswirken kann. Möglicherweise möchten Sie das Array zwischenspeichern, wenn es häufig aufgerufen wird.

Codebeispiel zum Zwischenspeichernvalues() .


Diese Antwort wurde bearbeitet, um das Feedback in die Kommentare aufzunehmen

Joachim Sauer
quelle
Ich habe diese Lösung implementiert und sie funktioniert bei mir nicht. Es wird zurückgegeben, dass der Ordnungswert nicht garantiert der Reihenfolge entspricht, in der die aufgezählten Typen hinzugefügt werden. Ich weiß nicht, was diese Antwort befürwortet, aber ich wollte die Leute trotzdem warnen
IcedDante
@IcesDante: Die Ordnungszahl entspricht mit Sicherheit der Reihenfolge der Aufzählungswerte in der Quelle. Wenn Sie ein anderes Verhalten beobachten, muss etwas anderes falsch sein. Meine obige Antwort ist jedoch aus allen in den anderen Antworten dargelegten Gründen nicht optimal.
Joachim Sauer
137

Dies ist mit ziemlicher Sicherheit eine schlechte Idee . Wenn die Ordnungszahl de facto beibehalten wird (z. B. weil jemand die URL mit einem Lesezeichen versehen hat), bedeutet dies, dass Sie die enumReihenfolge in Zukunft immer beibehalten müssen , was für Code-Betreuer auf der ganzen Linie möglicherweise nicht offensichtlich ist.

Warum nicht stattdessen die enumVerwendung verschlüsseln myEnumValue.name()(und über dekodieren ReportTypeEnum.valueOf(s))?

oxbow_lakes
quelle
24
Was ist, wenn Sie den Namen der Aufzählung ändern (aber die Reihenfolge beibehalten)?
Arne Evertsson
6
@Arne - Ich denke, dies ist viel weniger wahrscheinlich als eine unerfahrene Person, die mitkommt und valueentweder am Anfang oder an der richtigen alphabetischen / logischen Position ein a hinzufügt . (Mit logisch meine ich zum Beispiel, dass TimeUnitWerte eine logische Position haben)
oxbow_lakes
7
Ich ziehe es auf jeden Fall vor, die Reihenfolge der Aufzählungen anstelle des Namens meiner Aufzählung zu erzwingen. Deshalb bevorzuge ich es, die Ordnungszahl anstelle des Namens der Aufzählung in der Datenbank zu speichern. Darüber hinaus ist es besser, int-Manipulation als String zu verwenden ...
8
Genau. In einer öffentlichen API würde das Ändern des Namens einer Aufzählung die Abwärtskompatibilität beeinträchtigen, das Ändern der Reihenfolge jedoch nicht. Aus diesem Grund ist es sinnvoller, den Namen als "Schlüssel" zu verwenden
Noel
3
Das Speichern der Ordnungszahl erleichtert das Übersetzen Ihrer Ideen in andere Sprachen. Was ist, wenn Sie eine Komponente in C schreiben müssen?
QED
94

Wenn ich values()viel verwenden werde:

enum Suit {
   Hearts, Diamonds, Spades, Clubs;
   public static final Suit values[] = values();
}

Inzwischen überall.java:

Suit suit = Suit.values[ordinal];

Achten Sie auf Ihre Array-Grenzen.

QED
quelle
3
+1 Dies ist meiner Meinung nach bei weitem die beste Lösung, da man Ordnungszahlen vor allem in android.os.Message weitergeben kann.
Likejudo
9
Dies ist sehr besorgniserregend, da Arrays veränderlich sind . Obwohl values ​​[] endgültig ist, verhindert es nicht Suit.values[0] = Suit.Diamonds;irgendwo in Ihrem Code. Im Idealfall wird dies niemals passieren, aber das allgemeine Prinzip , keine veränderlichen Felder freizulegen, gilt weiterhin. Erwägen Sie für diesen Ansatz Collections.unmodifiableListstattdessen die Verwendung von oder dergleichen.
Mshnik
Wie wäre es mit - private statische endgültige Anzugwerte [] = Werte (); public static Suit [] getValues ​​() {Rückgabewerte; }
Pratyush
4
@Pratyush, das die Array-Variable unveränderlich machen würde, aber nicht ihren Inhalt. Ich könnte immer noch getValues ​​() [0] = SomethingElse;
Calabacin
Stimmen Sie zu, daher geht es nicht darum, das Suit values[]direkt oder indirekt (wie durch erwähnt getValues()) aufzudecken. Ich arbeite mit einer öffentlichen Methode herum, bei der der ordinalWert wie ein Argument gesendet werden muss und die SuitDarstellung von zurückgibt Suit values[]. Der Punkt hier (Fragekachel von Anfang an) war das Erstellen eines Aufzählungstyps aus einer Aufzählungszahl
Manuel Jordan
13

Ich stimme den meisten Menschen zu, dass die Verwendung von Ordnungszahlen wahrscheinlich eine schlechte Idee ist. Normalerweise löse ich dieses Problem, indem ich der Aufzählung einen privaten Konstruktor gebe, der beispielsweise einen DB-Wert annehmen und dann eine statische fromDbValueFunktion erstellen kann, die der in Jan's Antwort ähnelt.

public enum ReportTypeEnum {
    R1(1),
    R2(2),
    R3(3),
    R4(4),
    R5(5),
    R6(6),
    R7(7),
    R8(8);

    private static Logger log = LoggerFactory.getLogger(ReportEnumType.class);  
    private static Map<Integer, ReportTypeEnum> lookup;
    private Integer dbValue;

    private ReportTypeEnum(Integer dbValue) {
        this.dbValue = dbValue;
    }


    static {
        try {
            ReportTypeEnum[] vals = ReportTypeEnum.values();
            lookup = new HashMap<Integer, ReportTypeEnum>(vals.length);

            for (ReportTypeEnum  rpt: vals)
                lookup.put(rpt.getDbValue(), rpt);
         }
         catch (Exception e) {
             // Careful, if any exception is thrown out of a static block, the class
             // won't be initialized
             log.error("Unexpected exception initializing " + ReportTypeEnum.class, e);
         }
    }

    public static ReportTypeEnum fromDbValue(Integer dbValue) {
        return lookup.get(dbValue);
    }

    public Integer getDbValue() {
        return this.dbValue;
    }

}

Jetzt können Sie die Reihenfolge ändern, ohne die Suche zu ändern, und umgekehrt.

jmkelm08
quelle
DAS ist die richtige Antwort. Ich bin überrascht, dass es im Vergleich zu anderen direkteren, aber möglicherweise ungültigen Antworten (aufgrund von Codeänderungen in der Zukunft) so wenige Punkte gibt.
Calabacin
8

Sie können eine statische Nachschlagetabelle verwenden:

public enum Suit {
  spades, hearts, diamonds, clubs;

  private static final Map<Integer, Suit> lookup = new HashMap<Integer, Suit>();

  static{
    int ordinal = 0;
    for (Suit suit : EnumSet.allOf(Suit.class)) {
      lookup.put(ordinal, suit);
      ordinal+= 1;
    }
  }

  public Suit fromOrdinal(int ordinal) {
    return lookup.get(ordinal);
  }
}
Jan.
quelle
3
Siehe auch Aufzählungen .
Trashgod
11
Beeindruckend! Einfach wow! Das ist natürlich ordentlich, aber ... Sie wissen, der C-Programmierer in mir schreit vor Schmerz, als Sie sehen, dass Sie eine vollständige HashMap zuweisen und darin nur nachschlagen, um im Wesentlichen 4 Konstanten zu verwalten: Spaten, Herzen, Diamanten und Keulen! Ein AC-Programmierer würde jedem 1 Byte zuweisen: 'const char CLUBS = 0;' etc ... Ja, eine HashMap-Suche ist O (1), aber der Speicher- und CPU-Overhead einer HashMap macht sie in diesem Fall um viele Größenordnungen langsamer und ressourcenhungriger als das direkte Aufrufen von .values ​​()! Kein Wunder, dass Java so ein Gedächtnisfresser ist, wenn Leute so schreiben ...
Leszek
2
Nicht jedes Programm erfordert die Leistung eines Triple-A-Spiels. In vielen Fällen ist es gerechtfertigt, Speicher und CPU gegen Typensicherheit, Lesbarkeit, Wartbarkeit, plattformübergreifende Unterstützung, Speicherbereinigung usw. zu tauschen. Übergeordnete Sprachen existieren aus einem bestimmten Grund.
Jan
3
Wenn Ihr Schlüsselbereich jedoch immer ist 0...(n-1), ist ein Array weniger Code und auch besser lesbar. Der Leistungsschub ist nur ein Bonus. private static final Suit[] VALUES = values();und public Suit fromOrdinal(int ordinal) { return VALUES[ordinal]; }. Zusätzlicher Vorteil: Stürzt sofort bei ungültigen Ordnungszahlen ab, anstatt stillschweigend null zurückzugeben. (Nicht immer ein Vorteil. Aber oft.)
Thomas
4

Das benutze ich. Ich gebe nicht vor, dass es weit weniger "effizient" ist als die einfacheren Lösungen oben. Es wird eine viel klarere Ausnahmemeldung als "ArrayIndexOutOfBounds" angezeigt, wenn in der obigen Lösung ein ungültiger Ordnungswert verwendet wird.

Es nutzt die Tatsache, dass EnumSet javadoc angibt, dass der Iterator Elemente in ihrer natürlichen Reihenfolge zurückgibt. Es gibt eine Behauptung, wenn das nicht richtig ist.

Der JUnit4-Test zeigt, wie er verwendet wird.

 /**
 * convert ordinal to Enum
 * @param clzz may not be null
 * @param ordinal
 * @return e with e.ordinal( ) == ordinal
 * @throws IllegalArgumentException if ordinal out of range
 */
public static <E extends Enum<E> > E lookupEnum(Class<E> clzz, int ordinal) {
    EnumSet<E> set = EnumSet.allOf(clzz);
    if (ordinal < set.size()) {
        Iterator<E> iter = set.iterator();
        for (int i = 0; i < ordinal; i++) {
            iter.next();
        }
        E rval = iter.next();
        assert(rval.ordinal() == ordinal);
        return rval;
    }
    throw new IllegalArgumentException("Invalid value " + ordinal + " for " + clzz.getName( ) + ", must be < " + set.size());
}

@Test
public void lookupTest( ) {
    java.util.concurrent.TimeUnit tu = lookupEnum(TimeUnit.class, 3);
    System.out.println(tu);
}
gerardw
quelle
1

Das mache ich auf Android mit Proguard:

public enum SomeStatus {
    UNINITIALIZED, STATUS_1, RESERVED_1, STATUS_2, RESERVED_2, STATUS_3;//do not change order

    private static SomeStatus[] values = null;
    public static SomeStatus fromInteger(int i) {
        if(SomeStatus.values == null) {
            SomeStatus.values = SomeStatus.values();
        }
        if (i < 0) return SomeStatus.values[0];
        if (i >= SomeStatus.values.length) return SomeStatus.values[0];
        return SomeStatus.values[i];
    }
}

Es ist kurz und ich muss mir keine Sorgen um eine Ausnahme in Proguard machen

Irgendwer irgendwo
quelle
1

Sie können eine einfache Methode definieren wie:

public enum Alphabet{
    A,B,C,D;

    public static Alphabet get(int index){
        return Alphabet.values()[index];
    }
}

Und benutze es wie:

System.out.println(Alphabet.get(2));
Amir Fo
quelle
0
public enum Suit implements java.io.Serializable, Comparable<Suit>{
  spades, hearts, diamonds, clubs;
  private static final Suit [] lookup  = Suit.values();
  public Suit fromOrdinal(int ordinal) {
    if(ordinal< 1 || ordinal> 3) return null;
    return lookup[value-1];
  }
}

die Testklasse

public class MainTest {
    public static void main(String[] args) {
        Suit d3 = Suit.diamonds;
        Suit d3Test = Suit.fromOrdinal(2);
        if(d3.equals(d3Test)){
            System.out.println("Susses");
        }else System.out.println("Fails");
    }
}

Ich weiß es zu schätzen, dass Sie uns mitteilen, wenn Sie einen effizienteren Code haben. Meine Aufzählung ist riesig und wird ständig tausende Male aufgerufen.

Ar maj
quelle
Ich denke, Sie meinten "if (ordinal <1 || ordinal> 4) return null;"
Geowar
0

Eine Möglichkeit besteht darin, ExampleEnum valueOfOrdinal = ExampleEnum.values()[ordinal];das zu tun, was funktioniert, und es ist einfach, wie bereits erwähnt, ExampleEnum.values()für jeden Aufruf ein neues geklontes Array zurückzugeben. Das kann unnötig teuer sein. Wir können das lösen, indem wir das Array so zwischenspeichern ExampleEnum[] values = values(). Es ist auch "gefährlich", unser zwischengespeichertes Array ändern zu lassen. Jemand könnte schreiben ExampleEnum.values[0] = ExampleEnum.type2;Also würde ich es mit einer Accessor-Methode privat machen, die kein zusätzliches Kopieren durchführt.

private enum ExampleEnum{
    type0, type1, type2, type3;
    private static final ExampleEnum[] values = values();
    public static ExampleEnum value(int ord) {
        return values[ord];
    }
}

Sie würden verwenden ExampleEnum.value(ordinal), um den zugeordneten Aufzählungswert zu erhaltenordinal

Joe Pelletier
quelle
-1

Jede Aufzählung hat name (), was eine Zeichenfolge mit dem Namen des Aufzählungsmitglieds angibt.

Gegeben enum Suit{Heart, Spade, Club, Diamond}, Suit.Heart.name()wird geben Heart.

Jede Aufzählung hat eine valueOf()Methode, die einen Aufzählungstyp und eine Zeichenfolge verwendet, um die umgekehrte Operation auszuführen:

Enum.valueOf(Suit.class, "Heart")kehrt zurück Suit.Heart.

Warum jemand Ordnungszahlen verwenden würde, ist mir ein Rätsel. Es kann Nanosekunden schneller sein, aber es ist nicht sicher, wenn sich die Enum-Mitglieder ändern, da ein anderer Entwickler möglicherweise nicht weiß, dass ein Code auf Ordnungswerten basiert (insbesondere auf der in der Frage zitierten JSP-Seite dominiert der Netzwerk- und Datenbank-Overhead die Zeit, keine Ganzzahl über einer Zeichenfolge verwenden).

Tony BenBrahim
quelle
2
Weil das Vergleichen von ganzen Zahlen viel schneller ist als das Vergleichen von Zeichenfolgen?
HighCommander4
2
Die Ordnungszahlen ändern sich jedoch, wenn jemand die Aufzählung ändert (Memver hinzufügen / neu anordnen). Manchmal geht es um Sicherheit und nicht um Geschwindigkeit, insbesondere auf einer JSP-Seite, auf der die Netzwerklatenz das 1.000.000-fache des Unterschieds zwischen dem Vergleich eines Arrays von Ganzzahlen (einer Zeichenfolge) und einer einzelnen Ganzzahl beträgt.
Tony BenBrahim
toString kann überschrieben werden, sodass der Name der Aufzählung möglicherweise nicht zurückgegeben wird. Der Methodenname () gibt den Namen der Aufzählung an (er ist endgültig)
gerardw
Codekommentare und Anwendungsversionen sind ebenfalls eine Sache (vielleicht ist ein Dateiformat auf diese Weise einfacher und kleiner)
Joe Pelletier
Ich bin mir nicht sicher, warum dies abgelehnt wurde, wenn im Wesentlichen dasselbe empfohlen wird wie in der Antwort von oxbow_lakes. Auf jeden Fall sicherer als die Ordnungszahl.