Cast Int para enumerar en Java


335

¿Cuál es la forma correcta de convertir un Int en una enumeración en Java dada la siguiente enumeración?

public enum MyEnum
{
    EnumValue1,
    EnumValue2
}


MyEnum enumValue = (MyEnum) x; //Doesn't work???

@RyuS. Eso no es un duplicado de esta pregunta,
Mark Rotteveel

Respuestas:


588

Pruebe MyEnum.values()[x]dónde xdebe estar 0o 1, es decir, un ordinal válido para esa enumeración.

Tenga en cuenta que en Java las enumeraciones en realidad son clases (y los valores de enumeración son, por lo tanto, objetos) y, por lo tanto, no puede convertir una enumeración into incluso Integera una enumeración.


117
+1: es posible que desee almacenar en caché MyEnum.values()ya que es costoso. es decir, si lo llamas cientos de veces.
Peter Lawrey

66
@PeterLawrey Solo para completar, ¿puedes explicar por qué debería ser lento? No veo una razón obvia para ello.
Tarrasch

48
@Tarrasch como las matrices son mutables, los valores () deben devolver una copia de la matriz de elementos en caso de que lo cambie. Crear esta copia cada vez es relativamente costoso.
Peter Lawrey

99
@PeterLawrey ¡He estado usando mucho haskell últimamente (donde todo es inmutable)! Gracias por su explicación clara y concisa. :)
Tarrasch

¿Cómo obtener la 'x'?
Beeing Jk

161

MyEnum.values()[x]Es una operación costosa. Si el rendimiento es una preocupación, es posible que desee hacer algo como esto:

public enum MyEnum {
    EnumValue1,
    EnumValue2;

    public static MyEnum fromInteger(int x) {
        switch(x) {
        case 0:
            return EnumValue1;
        case 1:
            return EnumValue2;
        }
        return null;
    }
}

44
Si desea evitar el mantenimiento del conmutador, en la clase using: private final MyEnum [] myEnumValues ​​= MyEnum.values ​​(); Entonces uso: myEnum = myEnumValues ​​[i];
Gili Nachum

23
@GiliNachum lo dijo de una manera extraña, pero el problema con esta solución es la mantenibilidad. Va en contra del principio DRY, lo que significa que cada vez que se cambian los valores de enumeración (reordenados, valor (es) agregado (s), valor (es) eliminado) la declaración de cambio debe actualizarse simultáneamente. El comentario de Gili obliga a Java a mantener la coherencia con la lista de valores de enumeración, los cambios en los valores de enumeración no afectan en absoluto ese enfoque.
Dandre Allison

3
@LorenzoPolidori, ¿Puede explicar por qué lo considera MyEnum.values()[x]una operación costosa? No sé cómo funciona en detalles, pero para mí parece que acceder a un elemento en una matriz no sería gran cosa, también conocido como tiempo constante. Si la matriz tiene que compilarse, toma O (n) tiempo, que es el mismo tiempo de ejecución que su solución.
brunsgaard

1
@brunsgaard supongo que values()genera una nueva matriz cada vez, porque las matrices son mutables, por lo que no sería seguro devolver la misma varias veces. Las instrucciones de cambio no son necesariamente O (n), se pueden compilar para saltar tablas. Entonces las afirmaciones de Lorenzo parecen justificadas.
MikeFHay

1
La versión de @Johnson Gili no requiere ningún cambio de código adicional fuera de los cambios en la declaración de Enum. Si agrega un nuevo elemento a la enumeración, myEnum = myEnumValues[i]aún devolverá el ielemento th en la enumeración sin cambios.
Dandre Allison

45

Si desea dar sus valores enteros, puede usar una estructura como la siguiente

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

55
+1 porque resalta el hecho de que los valores no tienen que ser consecutivos.
rhavin

Además, esta parece ser la única respuesta que funciona si desea un conjunto escaso (o repetido) de valores enteros en lugar de utilizar los ordinales predeterminados de 0 .. (cuenta-1). Eso puede ser importante si está interactuando con el código existente, como a través de una red.
benkc

Vale la pena señalar que, como en las respuestas anteriores, es probable que valga la pena almacenar en caché el resultado de los valores (), para evitar una asignación de memoria y una copia de matriz cada vez que lo invoque.
benkc

1
Y, dependiendo de la longitud de su enumeración, es posible que desee crear un HashMap o usar una búsqueda binaria o algo para esta búsqueda, en lugar de hacer una búsqueda lineal cada vez.
benkc

2
Esta debería ser la forma correcta y la mejor práctica para convertir int a enum, y creo que puede simplificar el problema mediante la estática pública A GetValue (int _id) {for (A a: A.values ​​() {if (a.getId ( ) == _ id) {return a;}} return null;} Deshazte de las cosas None, isEmpty () y compare ().
Chris.Zou

35

Puedes intentarlo así.
Crear clase con elemento id.

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

Ahora obtenga esta enumeración usando id como int.

MyEnum myEnum = MyEnum.fromId(5);

19

Guardo en caché los valores y creo un método de acceso estático simple:

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

44
Esta es la solución que uso ahora. Pero en mi humilde opinión, es menos confuso si no le das al campo valuesel mismo nombre que el método values(). Yo uso cachedValuespara el nombre del campo.
ToolmakerSteve

2
Eficiente y elegante; copiado y pegado en mi proyecto :) Lo único que cambié es fromInt (int i), al que acabo de llamar desde (int i) porque es un poco redundante tener int dos veces en la firma.
pipedreambomb

1
¿Por qué no inicializar desde el principio? ¿Por qué esperar el primer golpe?
kaiser

9

Las enumeraciones de Java no tienen el mismo tipo de asignación de enumeración a int que tienen en C ++.

Dicho esto, todas las enumeraciones tienen un valuesmétodo que devuelve una matriz de posibles valores de enumeración, por lo que

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

Deberia trabajar. Es un poco desagradable y podría ser mejor no intentar convertir de ints a Enums (o viceversa) si es posible.


9

Esto no es algo que generalmente se hace, así que lo reconsideraría. Pero dicho esto, las operaciones fundamentales son: int -> enum usando EnumType.values ​​() [intNum], y enum -> int usando enumInst.ordinal ().

Sin embargo, dado que cualquier implementación de valores () no tiene más remedio que darle una copia de la matriz (las matrices java nunca son de solo lectura), será mejor que use un EnumMap para almacenar en caché la asignación enum -> int.


2
Re "Esto no es algo que generalmente se hace": caso común donde es útil: enum corresponde a valores int almacenados en una base de datos.
ToolmakerSteve

@ToolmakerSteve tiene toda la razón en que se requieren las asignaciones. ¿Pero le gustaría dejar ese tipo de codificación a algún mapeador OR o algún kit de herramientas / biblioteca?
Dilum Ranatunga


6

Aquí está la solución con la que planeo ir. Esto no solo funciona con enteros no secuenciales, sino que también debería funcionar con cualquier otro tipo de datos que desee utilizar como ID subyacente para sus valores de enumeración.

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

Solo necesito convertir las identificaciones en enumeraciones en momentos específicos (cuando se cargan datos de un archivo), por lo que no hay ninguna razón para mantener el Mapa en la memoria en todo momento. Si necesita que el mapa esté accesible en todo momento, siempre puede almacenarlo en caché como miembro estático de su clase Enum.


En mi humilde opinión, si me preocupara el uso de la memoria, crearía dinámicamente el HashMap, como @ossys pero con un código diferente cuando el caché es nulo, luego agregaría un segundo método clearCachedValuescuando termine de usarlo (eso establece el campo privado de nuevo a nulo). Considero MyEnum.fromInt(i)más fácil de entender que pasar un objeto de mapa.
ToolmakerSteve

6

En caso de que ayude a otros, la opción que prefiero, que no se encuentra aquí, utiliza la funcionalidad de Mapas de 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 el valor predeterminado que puede usar null, puede throw IllegalArgumentExceptiono fromIntpuede devolver un Optionalcomportamiento que prefiera.


3
Deberías mencionar que estás usando guayaba. O puede usar transmisiones:Map<Integer, MyEnum> reverseLookup = Arrays.stream(MyEnum.values()).collect(Collectors.toMap(MyEnum::getValue, Function.identity()));
shmosel

1
Podría querer definir un getValue()método también.
shmosel

@shmosel Vaya, me perdí la función getValue, gracias. Escribir versiones genéricas en stackoverflow no siempre funciona. Comentario agregado sobre el uso de Guava con un enlace. Prefiere el método de la guayaba a las corrientes.
Chad Befus

4

Puede iterar sobre values()enum y comparar el valor entero de enum con los dados ida continuación:

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

Y use así:
TestEnum x = TestEnum.getEnum(4);//Will return TestEnum.Value4
espero que esto ayude;)


3

Según la respuesta de @ChadBefus y el comentario de @shmosel, recomendaría usar esto. (Búsqueda eficiente, y funciona en 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

Una buena opción es evitar la conversión de inta enum: por ejemplo, si necesita el valor máximo, puede comparar x.ordinal () con y.ordinal () y devolver x o y en consecuencia. (Es posible que deba reordenar sus valores para que dicha comparación sea significativa).

Si eso no es posible, lo almacenaría MyEnum.values()en una matriz estática.


1
Si obtiene int de DB y desea convertirlo en Enum, que creo que es una tarea muy común, necesitará int -> enum conversion, IMO ..
Yuki Inoue

0

Esta es la misma respuesta que los médicos, pero muestra cómo eliminar el problema con las matrices mutables. Si usa este tipo de enfoque debido a la predicción de ramificación, primero tendrá un efecto de muy poco a cero y el código completo solo llama a los valores de matriz mutable () funcionan solo una vez. Como ambas variables son estáticas, tampoco consumirán n * memoria para cada uso de esta enumeración.

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 Creo que las excepciones son significativamente mejores si los límites son relativamente pocos.
Zoso

¿En qué se basa su creencia?
shmosel

0

En 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

Escribió esta implementación. Permite valores perdidos, valores negativos y mantiene el código consistente. El mapa también se almacena en caché. Utiliza una interfaz y necesita 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);
    }
}

Interfaz

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;
    }
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.