Como obter o valor Enum do índice em Java?

96

Eu tenho um enum em Java:

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

Quero acessar valores enum por índice, por exemplo

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

Como devo fazer isso?

jk_
fonte
12
Na ciência da computação, os índices começam em 0, não em 1 ;-)
fredoverflow
1
Você tem certeza que quer? Geralmente, você não deve tocar no ordinal, a não ser implementar estruturas de dados de baixo nível (e então, usar mecanismos alternativos, como nome, para persistência).
Tom Hawtin - tackline
Você também poderia ter usado as constantes na classe java.util.Calendar. Eles são numerados de 0 a 11 para janeiro a dezembro. Cuidado com o 12, pois é o mês de dezembro (algo a ver com o calendário lunar). Mas estou apenas curioso para saber por que reinventar a roda de constantes de mês que já vem "estoque" no JRE?
Chris Aldrich
2FredOverflow: Concordo, usei a indexação errada. 2Tom Hawtin: Sim, tenho certeza. Eu persisto os dados com alguma estrutura e recebo o índice inteiro, não o enum. 2Chris Aldrich: Este é apenas um exemplo simulado que não corresponde ao caso real.
jk_
A propósito, o Java 8 e posterior vem com um Monthenum integrado.
Basil Bourque de

Respostas:

229

Tente isto

Months.values()[index]
Harry Joy
fonte
37
Observe que clonará uma cópia da matriz de valores a cada vez, portanto, se você estiver chamando isso no loop interno do código sensível ao desempenho, você pode querer fazer uma cópia estática e usá-la.
Christopher Barber,
1
Estou confuso, então por que não gostaria de usar uma matriz em vez disso?
Anudeep Samaiya
@AnudeepSamaiya pode ser que queiramos usar constantes enum adequadas (Months.JAN) no código em vez de meses [1] em todos os lugares.
Harry Joy,
@Christopher Barber aqui está uma linha para "fazer uma cópia estática" public static final ArrayList<Months> ALL = new ArrayList<Month>() {{ for (Months m : Months.values()) add(m); }};Months i = ALL.get(index)
:,
Seria mais fácil simplesmente usar Months.values ​​(). Clone () ou se você estiver paranóico sobre a mutabilidade envolvê-la em uma lista imutável (consulte Coleções)
Christopher Barber
20

Aqui estão três maneiras de fazer isso.

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
fonte
5
public staticmutável (array e não final). Euw. E IllegalArgumentExceptionfaria muito mais sentido do que devolver uma nullbomba.
Tom Hawtin - tackline
2

Eu apenas tentei o mesmo e encontrei a seguinte solução:

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

A classe tem seus próprios valores salvos dentro de uma matriz, e eu uso a matriz para obter o enum na posição de índice. Como mencionado acima, as matrizes começam a contar a partir de 0, se você quiser que seu índice comece em '1', basta alterar estes dois métodos para:

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

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

Dentro do meu principal eu obtenho os países-objeto necessários com

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

que define currCountry como o último país, neste caso countries.KENTUCKY.

Lembre-se de que este código é muito afetado por ArrayOutOfBoundsExceptions se você estiver usando indicadores codificados para obter seus objetos.

Blauspecht
fonte
1

Recentemente tive o mesmo problema e usei a solução fornecida por Harry Joy. Essa solução só funciona com enumaration baseado em zero. Eu também não consideraria isso salvo, pois não lida com índices que estão fora do intervalo.

A solução que acabei usando pode não ser tão simples, mas é totalmente salva e não prejudica o desempenho do seu código, mesmo com grandes enums:

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

Se você tem certeza de que nunca estará fora do intervalo de seu índice e não deseja usar UNKNOWNcomo fiz acima, você também pode fazer:

public static Example getById(int id) {
        return enumById.get(id);
}
Mirko Brandt
fonte