Converter Int para enum em Java

335

Qual é a maneira correta de converter um Int para uma enumeração em Java, considerando a enumeração a seguir?

public enum MyEnum
{
    EnumValue1,
    EnumValue2
}


MyEnum enumValue = (MyEnum) x; //Doesn't work???
Maxim Gershkovich
fonte
@RyuS. Isso não é uma duplicata desta pergunta,
Mark Rotteveel 16/06

Respostas:

588

Tente MyEnum.values()[x]onde xdeve estar 0ou 1, ou seja, um ordinal válido para essa enumeração.

Note-se que em enums Java realmente são classes (e valores de enumeração são, portanto, objetos) e, portanto, você não pode lançar um intou até mesmo Integerpara um ENUM.

Thomas
fonte
117
+1: convém armazenar em cache MyEnum.values()como caro. ou seja, se você chamá-lo centenas de vezes.
Peter Lawrey
6
@PeterLawrey Apenas por completude, você pode explicar por que deve ser lento? Não vejo razão óbvia para isso.
Tarrasch
48
@ Tarrasch como matrizes são mutáveis, values ​​() devem retornar uma cópia da matriz de elementos para o caso de você alterá-la. Criar essa cópia a cada vez é relativamente caro.
Peter Lawrey
9
@ PeterLawrey eu tenho usado muito haskell ultimamente (onde tudo é imutável)! Obrigado pela sua explicação clara e concisa. :)
Tarrasch
como obter o 'x'?
Beeing Jk
161

MyEnum.values()[x]é uma operação cara. Se o desempenho for uma preocupação, convém fazer algo assim:

public enum MyEnum {
    EnumValue1,
    EnumValue2;

    public static MyEnum fromInteger(int x) {
        switch(x) {
        case 0:
            return EnumValue1;
        case 1:
            return EnumValue2;
        }
        return null;
    }
}
Lorenzo Polidori
fonte
44
Se você quiser evitar a manutenção do switch, na classe using: private final MyEnum [] myEnumValues ​​= MyEnum.values ​​(); Então use: myEnum = myEnumValues ​​[i];
Gili Nachum
23
A @GiliNachum disse isso de uma maneira estranha, mas o problema com esta solução é a manutenção. Isso vai contra o princípio DRY, que significa que sempre que os valores da enumeração são alterados (reordenados, valor agregado, valor removido), a instrução switch deve ser atualizada simultaneamente. O comentário de Gili força o Java a manter a consistência com a lista de valores de enumeração, alterações nos valores de enumeração não afetam essa abordagem.
Dandre Allison
3
@ LorenzoPolidori, você pode explicar por que considera MyEnum.values()[x]uma operação cara? Não sei como funciona em detalhes, mas para mim parece que acessar um elemento em uma matriz não seria grande coisa, também conhecido como tempo constante. Se a matriz precisar ser compilada, leva-se tempo O (n), que é o mesmo tempo de execução da sua solução.
Brunsgaard 04/10
1
@brunsgaard Suponho que values()gera uma nova matriz de cada vez, porque as matrizes são mutáveis, portanto não seria seguro retornar o mesmo várias vezes. As instruções de switch não são necessariamente O (n), elas podem ser compiladas para saltar tabelas. Portanto, as alegações de Lorenzo parecem justificadas.
precisa saber é o seguinte
1
A versão de @Johnson Gili não requer nenhuma alteração adicional de código fora das alterações na declaração Enum. Se você adicionar um novo item ao enum, myEnum = myEnumValues[i]ainda retornará o ith item no Enum sem alterações.
Dandre Allison
45

Se você deseja fornecer valores inteiros, pode usar uma estrutura como a abaixo

public enum A
{
        B(0),
        C(10),
        None(11);
        int id;
        private A(int i){id = i;}

        public int GetID(){return id;}
        public boolean IsEmpty(){return this.equals(A.None);}
        public boolean Compare(int i){return id == i;}
        public static A GetValue(int _id)
        {
            A[] As = A.values();
            for(int i = 0; i < As.length; i++)
            {
                if(As[i].Compare(_id))
                    return As[i];
            }
            return A.None;
        }
}
Médico
fonte
5
+1 porque destaca o fato de que os valores não precisam ser consecutivos.
rhavin
Além disso, esta parece ser a única resposta que funciona se você deseja um conjunto esparso (ou repetido) de valores inteiros, em vez de usar os ordinais padrão de 0 .. (contagem-1). Isso pode ser importante se você estiver interagindo com o código existente, como em uma rede.
benkc
Vale ressaltar que, como nas respostas acima, o armazenamento em cache do resultado de values ​​() provavelmente vale a pena, para evitar uma alocação de memória e cópia de matriz sempre que você o invocar.
benkc
1
E, dependendo do tamanho da sua enumeração, convém criar um HashMap ou usar uma pesquisa binária ou algo para essa pesquisa, em vez de fazer uma pesquisa linear todas as vezes.
benkc
2
Esse deve ser o caminho certo e a melhor prática para converter int para enum, e acho que você pode simplificar o problema por meio de estática pública A GetValue (int _id) {para (A a: A.values ​​() {if (a.getId ( ) == _ id) {return a;}} return null;} se livrar do None, isEmpty () e compare () coisas.
Chris.Zou
35

Você pode tentar assim.
Criar classe com o ID do elemento.

      public Enum MyEnum {
        THIS(5),
        THAT(16),
        THE_OTHER(35);

        private int id; // Could be other data type besides int
        private MyEnum(int id) {
            this.id = id;
        }

        public static MyEnum fromId(int id) {
                for (MyEnum type : values()) {
                    if (type.getId() == id) {
                        return type;
                    }
                }
                return null;
            }
      }

Agora, busque esse Enum usando o id como int.

MyEnum myEnum = MyEnum.fromId(5);
Piyush Ghediya
fonte
19

Coloco em cache os valores e crio um método simples de acesso estático:

public static enum EnumAttributeType {
    ENUM_1,
    ENUM_2;
    private static EnumAttributeType[] values = null;
    public static EnumAttributeType fromInt(int i) {
        if(EnumAttributeType.values == null) {
            EnumAttributeType.values = EnumAttributeType.values();
        }
        return EnumAttributeType.values[i];
    }
}
ossys
fonte
4
Esta é a solução que eu uso agora. Mas IMHO é menos confuso se você não der ao campo valueso mesmo nome que o método values(). Eu uso cachedValuespara o nome do campo.
Home
2
Eficiente e elegante; copiado e colado no meu projeto :) A única coisa que mudei é fromInt (int i), que acabei de chamar de (int i) porque é um pouco redundante ter int duas vezes na assinatura.
Pipedreambomb
1
por que não inicializar do começo? por que esperar o primeiro hit?
Kaiser #
9

As enumerações Java não têm o mesmo tipo de mapeamento enum-para-int do C ++.

Dito isto, todas as enumerações têm um valuesmétodo que retorna uma matriz de possíveis valores de enumeração, portanto

MyEnum enumValue = MyEnum.values()[x];

Deveria trabalhar. É um pouco desagradável e pode ser melhor não tentar converter de ints para Enums (ou vice-versa), se possível.

Cameron Skinner
fonte
9

Isso não é algo que geralmente é feito, então eu reconsideraria. Mas tendo dito isso, as operações fundamentais são: int -> enum usando EnumType.values ​​() [intNum] e enum -> int usando enumInst.ordinal ().

No entanto, como qualquer implementação de values ​​() não tem escolha a não ser fornecer uma cópia da matriz (as matrizes java nunca são somente leitura), você seria melhor atendido usando um EnumMap para armazenar em cache o mapeamento enum -> int.

Dilum Ranatunga
fonte
2
Re "Isso geralmente não é feito": Caso comum em que é útil: enum corresponde aos valores int armazenados em um banco de dados.
precisa
@ToolmakerSteve, você está absolutamente certo de que os mapeamentos são necessários. Mas você gostaria de deixar esse tipo de codificação para algum mapeador OR ou algum kit de ferramentas / biblioteca?
Dilum Ranatunga
7

Usar MyEnum enumValue = MyEnum.values()[x];

w.donahue
fonte
6

Aqui está a solução que pretendo seguir. Isso não funciona apenas com números inteiros não sequenciais, mas também com qualquer outro tipo de dados que você queira usar como o ID subjacente para seus valores de enumeração.

public Enum MyEnum {
    THIS(5),
    THAT(16),
    THE_OTHER(35);

    private int id; // Could be other data type besides int
    private MyEnum(int id) {
        this.id = id;
    }

    public int getId() {
        return this.id;
    }

    public static Map<Integer, MyEnum> buildMap() {
        Map<Integer, MyEnum> map = new HashMap<Integer, MyEnum>();
        MyEnum[] values = MyEnum.values();
        for (MyEnum value : values) {
            map.put(value.getId(), value);
        }

        return map;
    }
}

Eu só preciso converter IDs em enumerações em horários específicos (ao carregar dados de um arquivo), para que não haja motivo para manter o mapa na memória o tempo todo. Se você precisar que o mapa esteja acessível o tempo todo, sempre poderá armazená-lo em cache como membro estático da sua classe Enum.

jkindwall
fonte
IMHO, se eu estivesse preocupado com o uso de memória, criaria dinamicamente o @ossys do tipo HashMap, mas com código diferente quando o cache for nulo, em seguida, adicione um segundo método clearCachedValuesquando terminar de usá-lo (que define o campo privado novamente como nulo). Considero MyEnum.fromInt(i)mais fácil entender do que passar um objeto de mapa.
precisa
6

Caso ajude outras pessoas, a opção que eu prefiro, que não está listada aqui, usa a funcionalidade Mapas do Guava :

public enum MyEnum {
    OPTION_1(-66),
    OPTION_2(32);

    private int value;
    private MyEnum(final int value) {
        this.value = value;
    }

    public int getValue() {
        return this.value;
    }

    private static ImmutableMap<Integer, MyEnum> reverseLookup = 
            Maps.uniqueIndex(Arrays.asList(MyEnum.values())), MyEnum::getValue);

    public static MyEnum fromInt(final int id) {
        return reverseLookup.getOrDefault(id, OPTION_1);
    }
}

Com o padrão que você pode usar null, você pode throw IllegalArgumentExceptionou fromIntpode retornar um Optional, qualquer que seja o comportamento de sua preferência.

Chad Befus
fonte
3
Você deve mencionar que está usando o Goiaba. Ou você pode usar fluxos:Map<Integer, MyEnum> reverseLookup = Arrays.stream(MyEnum.values()).collect(Collectors.toMap(MyEnum::getValue, Function.identity()));
shmosel 16/17
1
getValue()Também pode querer definir um método.
shmosel
@shmosel Ops, perdi a função getValue, obrigado. Digitar versões genéricas no fluxo de empilhamento nem sempre ocorre. Adicionado comentário sobre o uso do Guava com um link. Prefira o método Guava aos fluxos.
Chad Befus
4

Você pode iterar sobre values()enum e comparar o valor inteiro de enum com o dado idabaixo:

public enum  TestEnum {
    None(0),
    Value1(1),
    Value2(2),
    Value3(3),
    Value4(4),
    Value5(5);

    private final int value;
    private TestEnum(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }

    public static TestEnum  getEnum(int value){
        for (TestEnum e:TestEnum.values()) {
            if(e.getValue() == value)
                return e;
        }
        return TestEnum.None;//For values out of enum scope
    }
}

E use assim:
TestEnum x = TestEnum.getEnum(4);//Will return TestEnum.Value4
espero que ajude;)

Yashar Aliabbasi
fonte
3

Com base na resposta de @ChadBefus e no comentário @shmosel, eu recomendo usar isso. (Pesquisa eficiente e funciona em java puro> = 8)

import java.util.stream.Collectors;
import java.util.function.Function;
import java.util.Map;
import java.util.Arrays;

public enum MyEnum {
    OPTION_1(-66),
    OPTION_2(32);

    private int value;
    private MyEnum(final int value) {
        this.value = value;
    }

    public int getValue() {
        return this.value;
    }

    private static Map<Integer, MyEnum> reverseLookup =
        Arrays.stream(MyEnum.values()).collect(Collectors.toMap(MyEnum::getValue, Function.identity()));

    public static MyEnum fromInt(final int id) {
        return reverseLookup.getOrDefault(id, OPTION_1);
    }
    public static void main(String[] args) {
        System.out.println(fromInt(-66).toString());
    }
}
Yuki Inoue
fonte
0

Uma boa opção é evitar a conversão de intpara enum: por exemplo, se você precisar do valor máximo, poderá comparar x.ordinal () com y.ordinal () e retornar x ou y correspondentemente. (Pode ser necessário reordenar os valores para tornar essa comparação significativa.)

Se isso não for possível, eu armazenaria MyEnum.values()em uma matriz estática.

18446744073709551615
fonte
1
Se você receber int de DB e quer convertê-lo em Enum, que eu acho que é uma tarefa muito comum, você vai precisar int -> conversão enum, IMO ..
Yuki Inoue
0

Essa é a mesma resposta que os médicos, mas mostra como eliminar o problema com matrizes mutáveis. Se você usar esse tipo de abordagem por causa da previsão de ramificação primeiro, terá muito pouco ou nenhum efeito e o código inteiro chamará apenas valores de matriz mutáveis ​​() apenas uma vez. Como ambas as variáveis ​​são estáticas, elas não consumirão n * memória para todos os usos dessa enumeração também.

private static boolean arrayCreated = false;
private static RFMsgType[] ArrayOfValues;

public static RFMsgType GetMsgTypeFromValue(int MessageID) {
    if (arrayCreated == false) {
        ArrayOfValues = RFMsgType.values();
    }

    for (int i = 0; i < ArrayOfValues.length; i++) {
        if (ArrayOfValues[i].MessageIDValue == MessageID) {
            return ArrayOfValues[i];
        }
    }
    return RFMsgType.UNKNOWN;
}
Ali Akdurak
fonte
0
enum MyEnum {
    A(0),
    B(1);
    private final int value;
    private MyEnum(int val) {this.value = value;}
    private static final MyEnum[] values = MyEnum.values();//cache for optimization
    public static final getMyEnum(int value) { 
        try {
            return values[value];//OOB might get triggered
        } catch (ArrayOutOfBoundsException e) {
        } finally {
            return myDefaultEnumValue;
        }
    }
}
Zoso
fonte
@shmosel Acredito que as exceções são significativamente melhores se os limites forem relativamente poucos.
Zoso
Em que sua crença se baseia?
Shmoel #
0

Em Kotlin:

enum class Status(val id: Int) {
    NEW(0), VISIT(1), IN_WORK(2), FINISHED(3), CANCELLED(4), DUMMY(5);

    companion object {
        private val statuses = Status.values().associateBy(Status::id)

        fun getStatus(id: Int): Status? = statuses[id]
    }
}

Uso:

val status = Status.getStatus(1)!!
CoolMind
fonte
0

Escreveu esta implementação. Permite valores ausentes, negativos e mantém o código consistente. O mapa também é armazenado em cache. Usa uma interface e precisa do Java 8.

Enum

public enum Command implements OrdinalEnum{
    PRINT_FOO(-7),
    PRINT_BAR(6),
    PRINT_BAZ(4);

    private int val;
    private Command(int val){
        this.val = val;
    }

    public int getVal(){
        return val;
    }

    private static Map<Integer, Command> map = OrdinalEnum.getValues(Command.class);
    public static Command from(int i){
        return map.get(i);
    }
}

Interface

public interface OrdinalEnum{
    public int getVal();

    @SuppressWarnings("unchecked")
    static <E extends Enum<E>> Map<Integer, E> getValues(Class<E> clzz){
        Map<Integer, E> m = new HashMap<>();
        for(Enum<E> e : EnumSet.allOf(clzz))
            m.put(((OrdinalEnum)e).getVal(), (E)e);

        return m;
    }
}
Gerrit Brink
fonte