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???

@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.


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

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

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

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

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.


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.


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


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.


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.


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


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

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.


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

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


@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)!!

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;
    }
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.