Jaki jest prawidłowy sposób rzutowania Int na wyliczenie w Javie, biorąc pod uwagę następujący wyliczenie?
public enum MyEnum
{
EnumValue1,
EnumValue2
}
MyEnum enumValue = (MyEnum) x; //Doesn't work???
Jaki jest prawidłowy sposób rzutowania Int na wyliczenie w Javie, biorąc pod uwagę następujący wyliczenie?
public enum MyEnum
{
EnumValue1,
EnumValue2
}
MyEnum enumValue = (MyEnum) x; //Doesn't work???
Odpowiedzi:
Spróbuj MyEnum.values()[x]
gdzie x
musi być 0
lub 1
, tj. Prawidłowy porządek dla tego wyliczenia.
Zauważ, że w Javie wyliczenia są w rzeczywistości klasami (a wartości wyliczenia są zatem obiektami), a zatem nie można rzutować int
ani nawet Integer
na wyliczenie.
MyEnum.values()
ponieważ jest drogi. tzn. jeśli nazwiesz to setki razy.
MyEnum.values()[x]
jest kosztowną operacją. Jeśli wydajność jest problemem, możesz chcieć zrobić coś takiego:
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]
za kosztowną operację. Nie wiem, jak to działa w szczegółach, ale wydaje mi się, że dostęp do elementu w tablicy nie byłby niczym wielkim, czyli ciągłym czasem. Jeśli tablica musi zostać skompilowana, zajmuje to O (n) czas, co jest tym samym czasem uruchamiania, co rozwiązanie.
values()
generuje nową tablicę za każdym razem, ponieważ tablice są zmienne, więc wielokrotne zwracanie tego samego nie byłoby bezpieczne. Instrukcje przełączania niekoniecznie są O (n), można je skompilować, aby przejść do tabel. Więc twierdzenia Lorenza wydają się uzasadnione.
myEnum = myEnumValues[i]
nadal zwróci ten i
element w wyliczeniu bez zmian.
Jeśli chcesz podać swoje liczby całkowite, możesz użyć struktury takiej jak poniżej
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;
}
}
Możesz spróbować w ten sposób.
Utwórz klasę o identyfikatorze elementu.
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;
}
}
Teraz pobierz to Enum używając id jako int.
MyEnum myEnum = MyEnum.fromId(5);
Zapisuję wartości w pamięci podręcznej i tworzę prostą metodę dostępu statycznego:
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
tej samej nazwy co metoda values()
. Używam cachedValues
nazwy pola.
Wyliczenia Java nie mają tego samego rodzaju odwzorowania wyliczania na int, co w C ++.
To powiedziawszy, wszystkie values
wyliczenia mają metodę, która zwraca tablicę możliwych wartości wyliczenia, więc
MyEnum enumValue = MyEnum.values()[x];
powinno działać. Jest to trochę nieprzyjemne i lepiej nie próbować konwertować z int
s na Enum
s (lub odwrotnie), jeśli to możliwe.
To nie jest coś, co zwykle się robi, więc ponownie się zastanowię. Ale powiedziawszy to, podstawowymi operacjami są: int -> enum przy użyciu EnumType.values () [intNum] oraz enum -> int przy użyciu enumInst.ordinal ().
Ponieważ jednak jakakolwiek implementacja wartości () nie ma innego wyjścia, jak dać kopię tablicy (tablice java nigdy nie są tylko do odczytu), lepiej byłoby użyć EnumMap do buforowania odwzorowania enum -> int.
Oto rozwiązanie, z którym planuję pójść. Działa to nie tylko z niesekwencyjnymi liczbami całkowitymi, ale powinno współpracować z każdym innym typem danych, którego możesz chcieć użyć jako podstawowego identyfikatora dla swoich wartości wyliczeniowych.
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;
}
}
Muszę tylko przekonwertować id na wyliczenia w określonych momentach (podczas ładowania danych z pliku), więc nie mam powodu, aby zawsze przechowywać Mapę w pamięci. Jeśli potrzebujesz, aby mapa była zawsze dostępna, zawsze możesz buforować ją jako statyczny element klasy Enum.
clearCachedValues
gdy skończysz z niej korzystać (która ustawia prywatne pole z powrotem na null). Uważam, że MyEnum.fromInt(i)
łatwiej to zrozumieć niż omijanie obiektu mapy.
W przypadku, gdy pomaga innym, preferowana przeze mnie opcja, która nie jest tutaj wymieniona, korzysta z funkcji Map 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);
}
}
Z domyślnym można użyć null
, można throw IllegalArgumentException
albo Twój fromInt
mogła zwracać Optional
, co zachowanie wolisz.
Map<Integer, MyEnum> reverseLookup = Arrays.stream(MyEnum.values()).collect(Collectors.toMap(MyEnum::getValue, Function.identity()));
getValue()
metodę.
Możesz iterować values()
wyliczanie i porównywać wartość całkowitą wyliczenia z podanymi id
jak poniżej:
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
}
}
I użyj tego w następujący sposób:
TestEnum x = TestEnum.getEnum(4);//Will return TestEnum.Value4
mam nadzieję, że to pomoże;)
Opierając się na odpowiedzi @ChadBefus i komentarzu @shmosel, polecam użyć tego. (Skuteczne wyszukiwanie i działa na czystej Javie> = 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());
}
}
Dobrą opcją jest uniknięcie konwersji z int
na enum
: na przykład, jeśli potrzebujesz maksymalnej wartości, możesz porównać x.ordinal () z y.ordinal () i odpowiednio zwrócić x lub y. (Może być konieczne ponowne uporządkowanie wartości, aby takie porównanie miało sens).
Jeśli nie jest to możliwe, zapisałbym MyEnum.values()
w tablicy statycznej.
Jest to ta sama odpowiedź, co lekarze, ale pokazuje, jak wyeliminować problem ze zmiennymi tablicami. Jeśli zastosujesz takie podejście ze względu na przewidywanie gałęzi, jeśli będzie miało bardzo niewielki do zera efekt, a cały kod wywoła funkcję tablic zmiennych () tylko raz. Ponieważ obie zmienne są statyczne, nie będą zużywać pamięci n * dla każdego użycia tego wyliczenia.
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;
}
}
}
W Kotlinie:
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]
}
}
Stosowanie:
val status = Status.getStatus(1)!!
Napisałem tę implementację. Pozwala to na brakujące wartości, wartości ujemne i utrzymuje spójność kodu. Mapa jest również buforowana. Korzysta z interfejsu i wymaga 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);
}
}
Berło
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;
}
}