Jak uzyskać wartość wyliczenia z indeksu w Javie?

96

Mam wyliczenie w Javie:

public enum Months
{
    JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC
}

Chcę uzyskać dostęp do wartości wyliczenia według indeksu, np

Months(1) = JAN;
Months(2) = FEB;
...

Jak mam to zrobić?

jk_
źródło
12
W informatyce indeksy zaczynają się od 0, a nie od 1 ;-)
fredoverflow
1
Czy na pewno chcesz? Generalnie nie powinieneś dotykać liczby porządkowej, poza implementacją struktur danych niskiego poziomu (a następnie użyj alternatywnych mechanizmów, takich jak nazwa, dla trwałości).
Tom Hawtin - tackline
Mogłeś również użyć stałych w klasie java.util.Calendar. Są ponumerowane od 0 do 11 od stycznia do grudnia. Uważaj na 12, ponieważ jest to grudzień (ma to związek z kalendarzem księżycowym). Ale jestem po prostu ciekawy, po co ponownie wymyślać koło stałych miesięcznych, które jest już dostępne w środowisku JRE?
Chris Aldrich,
2FredOverflow: Zgadzam się, użyłem niewłaściwego indeksowania. Tom Hawtin: Tak, jestem pewien. Utrwalam dane w jakimś frameworku i otrzymuję indeks w postaci liczby całkowitej, a nie wyliczenia. 2Chris Aldrich: To tylko fikcyjny przykład, który nie pasuje do rzeczywistego przypadku.
jk_
Nawiasem mówiąc, Java 8 i nowsze mają Monthwbudowane wyliczenie.
Basil Bourque

Odpowiedzi:

229

Spróbuj tego

Months.values()[index]
Harry Joy
źródło
37
Zauważ, że za każdym razem sklonuje kopię tablicy wartości, więc jeśli wywołujesz to w wewnętrznej pętli kodu wrażliwego na wydajność, możesz chcieć utworzyć kopię statyczną i użyć jej.
Christopher Barber
1
Jestem zdezorientowany, więc dlaczego nie miałbym zamiast tego używać tablicy?
Anudeep Samaiya
@AnudeepSamaiya może być, że chcemy używać właściwych stałych wyliczenia (Months.JAN) w kodzie zamiast miesięcy [1] wszędzie.
Harry Joy,
@Christopher Barber tutaj jest jednowierszowy sposób na „tworzenie statycznej kopii”: public static final ArrayList<Months> ALL = new ArrayList<Month>() {{ for (Months m : Months.values()) add(m); }};wtedy możesz uzyskać dostęp do elementów za pomocąMonths i = ALL.get(index)
muelleth
Byłoby łatwiej po prostu użyć Months.values ​​(). Clone () lub jeśli masz paranoję na temat zmienności, aby zawinąć ją w niezmienną listę (patrz Kolekcje)
Christopher Barber
20

Oto trzy sposoby, aby to zrobić.

public enum Months {
    JAN(1), FEB(2), MAR(3), APR(4), MAY(5), JUN(6), JUL(7), AUG(8), SEP(9), OCT(10), NOV(11), DEC(12);


    int monthOrdinal = 0;

    Months(int ord) {
        this.monthOrdinal = ord;
    }

    public static Months byOrdinal2ndWay(int ord) {
        return Months.values()[ord-1]; // less safe
    }

    public static Months byOrdinal(int ord) {
        for (Months m : Months.values()) {
            if (m.monthOrdinal == ord) {
                return m;
            }
        }
        return null;
    }
    public static Months[] MONTHS_INDEXED = new Months[] { null, JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC };

}




import static junit.framework.Assert.assertEquals;

import org.junit.Test;

public class MonthsTest {

@Test
public void test_indexed_access() {
    assertEquals(Months.MONTHS_INDEXED[1], Months.JAN);
    assertEquals(Months.MONTHS_INDEXED[2], Months.FEB);

    assertEquals(Months.byOrdinal(1), Months.JAN);
    assertEquals(Months.byOrdinal(2), Months.FEB);


    assertEquals(Months.byOrdinal2ndWay(1), Months.JAN);
    assertEquals(Months.byOrdinal2ndWay(2), Months.FEB);
}

}
Trever Shick
źródło
5
public staticmutable (zarówno tablicowe, jak i nie final). Euw. I IllegalArgumentExceptionmiałoby to o wiele więcej sensu niż zwrot nullbomby.
Tom Hawtin - tackline
2

Właśnie wypróbowałem to samo i znalazłem następujące rozwiązanie:

public enum Countries {
    TEXAS,
    FLORIDA,
    OKLAHOMA,
    KENTUCKY;

    private static Countries[] list = Countries.values();

    public static Countries getCountry(int i) {
        return list[i];
    }

    public static int listGetLastIndex() {
        return list.length - 1;
    }
}

Klasa ma własne wartości zapisane w tablicy, a ja używam tablicy, aby uzyskać wyliczenie w pozycji indeksu. Jak wspomniano powyżej, tablice zaczynają liczyć od 0, jeśli chcesz, aby indeks zaczynał się od „1”, po prostu zmień te dwie metody na:

public static String getCountry(int i) {
    return list[(i - 1)];
}

public static int listGetLastIndex() {
    return list.length;
}

W moim Main dostaję potrzebne kraje-obiekt z

public static void main(String[] args) {
   int i = Countries.listGetLastIndex();
   Countries currCountry = Countries.getCountry(i);
}

która ustawia currCountry na ostatni kraj, w tym przypadku Countries.KENTUCKY.

Pamiętaj tylko, że na ten kod mają duży wpływ wyjątki ArrayOutOfBoundsExceptions, jeśli używasz zakodowanych na stałe wskaźników do uzyskania obiektów.

Blauspecht
źródło
1

Niedawno miałem ten sam problem i korzystałem z rozwiązania dostarczonego przez Harry'ego Joy. To rozwiązanie działa jednak tylko z wyliczaniem od zera. Nie uważałbym również, że jest zapisywany, ponieważ nie obsługuje indeksów, które są poza zakresem.

Rozwiązanie, którego użyłem, może nie być tak proste, ale jest całkowicie bezpieczne i nie zaszkodzi wydajności twojego kodu nawet przy dużych wyliczeniach:

public enum Example {

    UNKNOWN(0, "unknown"), ENUM1(1, "enum1"), ENUM2(2, "enum2"), ENUM3(3, "enum3");

    private static HashMap<Integer, Example> enumById = new HashMap<>();
    static {
        Arrays.stream(values()).forEach(e -> enumById.put(e.getId(), e));
    }

    public static Example getById(int id) {
        return enumById.getOrDefault(id, UNKNOWN);
    }

    private int id;
    private String description;

    private Example(int id, String description) {
        this.id = id;
        this.description= description;
    }

    public String getDescription() {
        return description;
    }

    public int getId() {
        return id;
    }
}

Jeśli jesteś pewien, że nigdy nie wyjdziesz poza zakres swojego indeksu i nie chcesz używać go UNKNOWNtak, jak zrobiłem powyżej, możesz oczywiście również:

public static Example getById(int id) {
        return enumById.get(id);
}
Mirko Brandt
źródło