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???
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???
Respostas:
Tente MyEnum.values()[x]
onde x
deve estar 0
ou 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 int
ou até mesmo Integer
para um ENUM.
MyEnum.values()
como caro. ou seja, se você chamá-lo centenas de vezes.
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;
}
}
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.
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.
myEnum = myEnumValues[i]
ainda retornará o i
th item no Enum sem alterações.
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;
}
}
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);
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];
}
}
values
o mesmo nome que o método values()
. Eu uso cachedValues
para o nome do campo.
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 values
mé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 int
s para Enum
s (ou vice-versa), se possível.
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.
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.
clearCachedValues
quando 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.
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 IllegalArgumentException
ou fromInt
pode retornar um Optional
, qualquer que seja o comportamento de sua preferência.
Map<Integer, MyEnum> reverseLookup = Arrays.stream(MyEnum.values()).collect(Collectors.toMap(MyEnum::getValue, Function.identity()));
getValue()
Também pode querer definir um método.
Você pode iterar sobre values()
enum e comparar o valor inteiro de enum com o dado id
abaixo:
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;)
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());
}
}
Uma boa opção é evitar a conversão de int
para 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.
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;
}
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;
}
}
}
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)!!
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;
}
}