Qual è il modo corretto di lanciare un Int in un enum in Java dato il seguente enum?
public enum MyEnum
{
EnumValue1,
EnumValue2
}
MyEnum enumValue = (MyEnum) x; //Doesn't work???
Qual è il modo corretto di lanciare un Int in un enum in Java dato il seguente enum?
public enum MyEnum
{
EnumValue1,
EnumValue2
}
MyEnum enumValue = (MyEnum) x; //Doesn't work???
Risposte:
Prova MyEnum.values()[x]
dove x
deve essere 0
o 1
, cioè un ordinale valido per quell'enum.
Si noti che in Java gli enum in realtà sono classi (e quindi i valori enum sono oggetti) e quindi non è possibile eseguire il cast di un enum int
o addirittura Integer
di un enum.
MyEnum.values()
come costoso. cioè se lo chiami centinaia di volte.
MyEnum.values()[x]
è un'operazione costosa. Se la performance è un problema, potresti voler fare qualcosa del genere:
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]
un'operazione costosa. Non so come funzioni nei dettagli, ma per me sembra che accedere a un elemento in un array non sarebbe un grosso problema, alias tempo costante. Se l'array deve essere compilato, ci vuole O (n) tempo, che è lo stesso tempo di esecuzione della soluzione.
values()
generi ogni volta un nuovo array, perché gli array sono mutabili, quindi non sarebbe sicuro restituire lo stesso più volte. Le istruzioni switch non sono necessariamente O (n), possono essere compilate per saltare le tabelle. Quindi le affermazioni di Lorenzo sembrano giustificate.
myEnum = myEnumValues[i]
restituirà comunque l' i
elemento th nell'enum senza modifiche.
Se vuoi dare i tuoi valori interi, puoi usare una struttura come sotto
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;
}
}
Puoi provare così.
Crea classe con ID 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;
}
}
Ora prendi questo Enum usando id come int.
MyEnum myEnum = MyEnum.fromId(5);
Memorizzo nella cache i valori e creo un semplice metodo di accesso statico:
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
lo stesso nome del metodo values()
. Io uso cachedValues
per il nome del campo.
Gli enum Java non hanno lo stesso tipo di mapping enum-to-int che hanno in C ++.
Detto questo, tutti gli enum hanno un values
metodo che restituisce una matrice di possibili valori enum, quindi
MyEnum enumValue = MyEnum.values()[x];
dovrebbe funzionare. È un po 'brutto e potrebbe essere meglio non provare a convertire da int
s a Enum
s (o viceversa) se possibile.
Questo non è qualcosa che di solito viene fatto, quindi riconsidererei. Detto questo, le operazioni fondamentali sono: int -> enum usando EnumType.values () [intNum] e enum -> int usando enumInst.ordinal ().
Tuttavia, poiché qualsiasi implementazione di valori () non ha altra scelta che darti una copia dell'array (gli array java non sono mai di sola lettura), verrai servito meglio usando una EnumMap per memorizzare nella cache il mapping enum -> int.
Ecco la soluzione con cui ho intenzione di andare. Questo non funziona solo con numeri interi non sequenziali, ma dovrebbe funzionare con qualsiasi altro tipo di dati che potresti voler usare come ID sottostante per i tuoi valori enum.
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;
}
}
Devo solo convertire gli ID in enum in momenti specifici (durante il caricamento di dati da un file), quindi non c'è motivo per me di mantenere la mappa in memoria in ogni momento. Se è necessario che la mappa sia sempre accessibile, è sempre possibile memorizzarla nella cache come membro statico della classe Enum.
clearCachedValues
quando hai finito di usarlo (che riporta il campo privato su null). Considero MyEnum.fromInt(i)
più facile da capire che passare intorno a un oggetto mappa.
Nel caso in cui aiuti gli altri, l'opzione che preferisco, che non è elencata qui, utilizza la funzionalità Mappe di 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);
}
}
Con l'impostazione predefinita che puoi utilizzare null
, puoi throw IllegalArgumentException
o puoi fromInt
restituire un Optional
comportamento qualunque tu preferisca.
Map<Integer, MyEnum> reverseLookup = Arrays.stream(MyEnum.values()).collect(Collectors.toMap(MyEnum::getValue, Function.identity()));
getValue()
metodo.
Puoi iterare su values()
enum e confrontare il valore intero di enum con dato id
come di seguito:
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 usa così:
TestEnum x = TestEnum.getEnum(4);//Will return TestEnum.Value4
spero che questo aiuti;)
Sulla base della risposta di @ChadBefus e del commento di @shmosel, consiglierei di usare questo. (Ricerca efficiente e funziona su 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());
}
}
Una buona opzione è quella di evitare la conversione da int
aenum
: ad esempio, se hai bisogno del valore massimo, puoi confrontare x.ordinal () a y.ordinal () e restituire x o y di conseguenza. (Potrebbe essere necessario riordinare i valori per rendere significativo tale confronto.)
Se ciò non fosse possibile, memorizzerei MyEnum.values()
in un array statico.
Questa è la stessa risposta dei medici ma mostra come eliminare il problema con array mutabili. Se si utilizza questo tipo di approccio a causa della previsione della diramazione, se si avrà un effetto minimo o nullo e l'intero codice chiama i valori di matrice mutabili () solo una volta. Poiché entrambe le variabili sono statiche, non consumeranno n * memoria per ogni utilizzo di questa enumerazione.
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;
}
}
}
Ha scritto questa implementazione. Consente valori mancanti, valori negativi e mantiene coerente il codice. Anche la mappa è memorizzata nella cache. Utilizza un'interfaccia e necessita di 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);
}
}
Interfaccia
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;
}
}