Konwertuj z enum ordinal na enum

315

Mam typ wyliczania, ReportTypeEnumktóry jest przekazywany między metodami we wszystkich moich klasach, ale muszę przekazać to na adres URL, więc używam metody porządkowej, aby uzyskać wartość int. Po umieszczeniu go na innej stronie JSP muszę przekonwertować go z powrotem na wersję, ReportTypeEnumaby móc kontynuować przekazywanie.

Jak przekonwertować numer porządkowy na ReportTypeEnum?

Korzystanie z Java 6 SE.

Lennie
źródło
1
Do tej pory nie ma Java 6 EE (AFAIK). Istnieje Java SE 6 i Java EE 5.
Hosam Aly

Odpowiedzi:

632

Aby przekonwertować porządek porządkowy na jego enum represantację, możesz to zrobić:

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

Zwróć uwagę na granice tablicy.

Zauważ, że każde wywołanie values()zwraca nowo sklonowaną tablicę, co może mieć negatywny wpływ na wydajność. Możesz buforować tablicę, jeśli będzie często wywoływana.

Przykład kodu, jak buforowaćvalues() .


Ta odpowiedź została edytowana w celu uwzględnienia informacji zwrotnych podanych w komentarzach

Joachim Sauer
źródło
Wdrożyłem to rozwiązanie i nie działa dla mnie. Zwraca wartość porządkową, która nie gwarantuje zgodności z kolejnością dodawania wyliczonych typów. Nie wiem, o co nam chodzi w tej odpowiedzi, ale mimo to chciałem ostrzec ludzi
IcedDante
@IcesDante: porządek z pewnością gwarantuje kolejność wartości wyliczenia w źródle. Jeśli zaobserwujesz inne zachowanie, coś innego musi być nie tak. Moja odpowiedź powyżej jest jednak nieoptymalna z wszystkich powodów przedstawionych w innych odpowiedziach.
Joachim Sauer
137

To prawie na pewno zły pomysł . Oczywiście, jeśli porządek porządkowy jest de facto utrzymywany (np. Ponieważ ktoś dodał adres URL do zakładek) - oznacza to, że zawsze musisz zachować enumporządek w przyszłości, co może nie być oczywiste dla opiekunów kodu w dalszej kolejności.

Dlaczego zamiast tego nie zakodować za enumpomocą myEnumValue.name()(i dekodować przez ReportTypeEnum.valueOf(s))?

oxbow_lakes
źródło
24
Co się stanie, jeśli zmienisz nazwę wyliczenia (ale zachowaj kolejność)?
Arne Evertsson,
6
@Arne - Myślę, że jest to znacznie mniej prawdopodobne niż jakaś niedoświadczona osoba przychodząca i dodająca valuealbo na początku, albo we właściwej pozycji alfabetycznej / logicznej. (Przez logiczne rozumiem na przykład TimeUnit, że wartości mają logiczną pozycję)
oxbow_lakes
7
Z pewnością wolę wymuszać porządek wyliczeń zamiast nazwy mojego wyliczenia ... dlatego wolę przechowywać porządek zamiast nazwy wyliczenia w bazie danych. Co więcej, lepiej jest używać manipulacji int niż String ...
8
Zgadzam się. W publicznym interfejsie API zmiana nazwy Enum spowodowałaby naruszenie kompatybilności wstecznej, ale zmiana kolejności nie. Z tego powodu bardziej sensowne jest używanie nazwy jako „klucza”
Noel
3
Przechowywanie porządkowej ułatwia tłumaczenie twoich pomysłów na inne języki. Co jeśli musisz napisać jakiś komponent w C?
QED,
94

Jeśli zamierzam values()dużo używać:

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

Tymczasem wherever.java:

Suit suit = Suit.values[ordinal];

Uważaj na granice tablicy.

CO BYŁO DO OKAZANIA
źródło
3
+1 jest to zdecydowanie najlepsze rozwiązanie IMHO, ponieważ można przekazywać porządki, szczególnie w android.os.Message.
likejudo
9
Jest to bardzo niepokojące, ponieważ tablice są zmienne . Mimo że wartości [] są ostateczne, nie zapobiega to Suit.values[0] = Suit.Diamonds;gdzieś w kodzie. Najlepiej, aby to nigdy się nie zdarzyło, ale nadal obowiązuje ogólna zasada nie ujawniania zmiennych pól . W przypadku tego podejścia rozważ użycie Collections.unmodifiableListzamiast tego lub podobnego.
Mshnik,
A co powiesz na - prywatne statyczne końcowe wartości koloru [] = wartości (); public static Suit [] getValues ​​() {zwracane wartości; }
Pratyush,
4
@ Pratyush, który sprawi, że zmienna tablicowa będzie niezmienna, ale nie jej zawartość. Mógłbym nadal zrobić getValues ​​() [0] = somethingElse;
Calabacin,
Zgadzam się, więc chodzi o to, aby nie ujawniać Suit values[]bezpośrednio lub pośrednio (jak wspomniano getValues()), pracuję z publiczną metodą, do której ordinalnależy wysłać wartość jako argument i zwrócić Suitreprezentację Suit values[]. Punktem tutaj (płytka pytania od samego początku) było stworzenie typu enum z enum ordinal
Manuel Jordan
13

Zgadzam się z większością osób, że używanie porządkowej jest prawdopodobnie złym pomysłem. Zwykle rozwiązuję ten problem, dając enumowi prywatny konstruktor, który może na przykład wziąć wartość DB, a następnie utworzyć funkcję statyczną fromDbValuepodobną do tej w odpowiedzi Jana.

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

}

Teraz możesz zmienić kolejność bez zmiany wyszukiwania i odwrotnie.

jmkelm08
źródło
To jest właściwa odpowiedź. Dziwi mnie, że otrzymało tak mało punktów w porównaniu z innymi bardziej bezpośrednimi, ale potencjalnie nieprawidłowymi odpowiedziami (ze względu na zmiany kodu w przyszłości).
Calabacin,
8

Ty mógł używać statycznego tabeli odnośników:

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
źródło
3
Zobacz także Enums .
trashgod
11
Łał! Po prostu łał! To jest oczywiście fajne, ale ... no wiesz - programista C we mnie krzyczy z bólu, widząc, że przydzielasz pełnoprawną HashMapę i przeprowadzasz w niej wszystkie wyszukiwania, aby po prostu zarządzać 4 stałymi: pikami, sercami, diamenty i kluby! Programista AC przydzieli 1 bajt dla każdego: „const char CLUBS = 0;” itd ... Tak, wyszukiwanie HashMap to O (1), ale narzut pamięci i procesora HashMap, w tym przypadku sprawia, że ​​jest o wiele rzędów wielkości wolniejszy i wymaga więcej zasobów niż bezpośrednie wywoływanie .values ​​()! Nic dziwnego, że Java to taki świnia pamięci, jeśli ludzie tak piszą ...
Leszek
2
Nie każdy program wymaga wykonania potrójnej gry typu A. W wielu przypadkach wymiana pamięci i procesora na bezpieczeństwo typu, czytelność, łatwość konserwacji, obsługę wielu platform, wyrzucanie elementów bezużytecznych itp. Jest uzasadnione. Nie bez powodu istnieją języki wyższego poziomu.
stycznia
3
Ale jeśli twój kluczowy zakres jest zawsze 0...(n-1), to tablica ma mniej kodu i jest bardziej czytelna; wzrost wydajności to tylko bonus. private static final Suit[] VALUES = values();a public Suit fromOrdinal(int ordinal) { return VALUES[ordinal]; }. Dodatkowa zaleta: natychmiast zawiesza się przy nieprawidłowych porządkach, zamiast po cichu zwracać wartość null. (Nie zawsze jest to zaleta. Ale często.)
Thomas
4

Tego używam. Nie udaję, że jest on znacznie mniej „wydajny” niż prostsze rozwiązania powyżej. Zapewnia to znacznie wyraźniejszy komunikat wyjątku niż „ArrayIndexOutOfBounds”, gdy w powyższym rozwiązaniu użyto niepoprawnej wartości porządkowej.

Wykorzystuje fakt, że EnumSet javadoc określa, że ​​iterator zwraca elementy w ich naturalnej kolejności. Jest twierdzenie, jeśli to nie jest poprawne.

Test JUnit4 pokazuje, jak jest używany.

 /**
 * 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
źródło
1

Oto, co robię na Androidzie z 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];
    }
}

jest krótki i nie muszę się martwić o wyjątek w Proguard

Ktoś gdzieś
źródło
1

Możesz zdefiniować prostą metodę, taką jak:

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

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

I używaj go w następujący sposób:

System.out.println(Alphabet.get(2));
Amir Fo
źródło
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];
  }
}

klasa testowa

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

Doceniam to, że dzielisz się z nami, jeśli masz bardziej wydajny kod. Moje wyliczenie jest ogromne i ciągle wywoływane tysiące razy.

Ar maj
źródło
Myślę, że miałeś na myśli „if (ordinal <1 || ordinal> 4) return null;”
geowar
0

Tak więc jednym ze sposobów jest zrobienie tego, ExampleEnum valueOfOrdinal = ExampleEnum.values()[ordinal];co działa, a jego łatwe, jak wspomniano wcześniej, ExampleEnum.values()zwraca nową sklonowaną tablicę dla każdego wywołania. To może być niepotrzebnie drogie. Możemy to rozwiązać, buforując tablicę w taki sposób ExampleEnum[] values = values(). Jest również „niebezpieczne”, aby zezwolić na modyfikację naszej pamięci podręcznej. Ktoś mógłby napisać, ExampleEnum.values[0] = ExampleEnum.type2;więc uczynię to prywatnym za pomocą metody akcesorium, która nie wykonuje dodatkowego kopiowania.

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

Użyłbyś, ExampleEnum.value(ordinal)aby uzyskać wartość wyliczenia związaną zordinal

Joe Pelletier
źródło
-1

Każde wyliczenie ma nazwę (), która daje ciąg o nazwie członka wyliczenia.

Biorąc pod uwagę enum Suit{Heart, Spade, Club, Diamond}, Suit.Heart.name()da Heart.

Każde wyliczenie ma valueOf() metodę, która wykonuje typ wyliczenia i ciąg znaków, aby wykonać operację odwrotną:

Enum.valueOf(Suit.class, "Heart")zwraca Suit.Heart.

Dlaczego ktokolwiek używałby porządków, jest poza mną. Może być szybszy o nanosekundy, ale zmiana enum nie jest bezpieczna, ponieważ inny programista może nie wiedzieć, że jakiś kod opiera się na wartościach porządkowych (zwłaszcza na stronie JSP cytowanej w pytaniu, narzut sieci i bazy danych całkowicie dominuje czas, bez używania liczby całkowitej w ciągu).

Tony BenBrahim
źródło
2
Ponieważ porównywanie liczb całkowitych jest znacznie szybsze niż porównywanie ciągów?
HighCommander4
2
Ale porządki zmienią się, jeśli ktoś zmodyfikuje wyliczanie (dodaje / zmienia kolejność notatek). Czasami chodzi o bezpieczeństwo, a nie szybkość, szczególnie na stronie JSP, gdzie opóźnienie sieci wynosi 1 000 000 razy więcej niż porównanie tablicy liczb całkowitych (ciągów) z pojedynczą liczbą całkowitą.
Tony BenBrahim,
toString można zastąpić, więc nie może zwrócić nazwy wyliczenia. Metoda name () podaje nazwę enum (jest ostateczna)
gerardw
komentarze do kodu i wersje aplikacji również są czymś (może format pliku jest w ten sposób prostszy i mniejszy)
Joe Pelletier
Nie jestem pewien, dlaczego zostało to zanegowane, skoro zasadniczo zaleca to samo, co w odpowiedzi oxbow_lakes. Zdecydowanie bezpieczniejsze niż używanie porządkowej.