Cast Int pour énumérer en Java


335

Quelle est la bonne façon de convertir un Int en une énumération en Java étant donné l'énumération suivante?

public enum MyEnum
{
    EnumValue1,
    EnumValue2
}


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

@RyuS. Ce n'est pas un double de cette question,
Mark Rotteveel

Réponses:


588

Essayez MyEnum.values()[x]xdoit être 0ou 1, c'est-à-dire un ordinal valide pour cette énumération.

Notez qu'en Java, les énumérations sont en fait des classes (et les valeurs d'énumération sont donc des objets) et donc vous ne pouvez pas convertir un intou même Integeren une énumération.


117
+1: Vous voudrez peut-être mettre MyEnum.values()en cache car c'est cher. c'est-à-dire si vous l'appelez des centaines de fois.
Peter Lawrey

6
@PeterLawrey Juste pour être complet, pouvez-vous expliquer pourquoi cela devrait être lent? Je ne vois aucune raison évidente à cela.
Tarrasch

48
@Tarrasch étant donné que les tableaux sont modifiables, values ​​() doit renvoyer une copie du tableau d'éléments au cas où vous le modifieriez. La création de cette copie à chaque fois est relativement coûteuse.
Peter Lawrey

9
@PeterLawrey J'ai utilisé trop de haskell récemment (où tout est immuable)! Merci pour votre explication claire et concise. :)
Tarrasch

comment obtenir le «x»?
Beeing Jk

161

MyEnum.values()[x]est une opération coûteuse. Si les performances sont un problème, vous voudrez peut-être faire quelque chose comme ceci:

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 vous voulez éviter la maintenance du commutateur, alors sur la classe using: private final MyEnum [] myEnumValues ​​= MyEnum.values ​​(); Puis utilisation: myEnum = myEnumValues ​​[i];
Gili Nachum

23
@GiliNachum l'a dit d'une manière étrange, mais le problème avec cette solution est la maintenabilité. Cela va à l'encontre du principe DRY, ce qui signifie que chaque fois que les valeurs d'énumération sont modifiées (réorganisées, valeur (s) ajoutée (s), valeur (s) supprimée (s)), l'instruction switch doit être mise à jour simultanément. Le commentaire de Gili oblige Java à maintenir la cohérence avec la liste des valeurs d'énumération, les modifications apportées aux valeurs d'énumération n'affectent pas du tout cette approche.
Dandre Allison

3
@ LorenzoPolidori, pouvez-vous expliquer pourquoi vous considérez MyEnum.values()[x]comme une opération coûteuse. Je ne sais pas comment cela fonctionne dans les détails, mais pour moi, il semble que l'accès à un élément dans un tableau ne serait pas très grave, alias temps constant. Si le tableau doit être construit, cela prend du temps O (n), qui est le même temps d'exécution que votre solution.
brunsgaard

1
@brunsgaard Je suppose que values()génère un nouveau tableau à chaque fois, car les tableaux sont mutables, il ne serait donc pas sûr de retourner le même plusieurs fois. Les instructions switch ne sont pas nécessairement O (n), elles peuvent être compilées pour sauter des tables. Les affirmations de Lorenzo semblent donc justifiées.
MikeFHay

1
La version de @Johnson Gili ne nécessite aucune modification de code supplémentaire en dehors des modifications de la déclaration Enum. Si vous ajoutez un nouvel élément à l'énumération, myEnum = myEnumValues[i]le même iélément sera renvoyé dans l'énumération sans modifications.
Dandre Allison

45

Si vous voulez donner vos valeurs entières, vous pouvez utiliser une structure comme ci-dessous

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 car cela met en évidence le fait que les valeurs n'ont pas à être consécutives.
rhavin

En outre, cela semble être la seule réponse qui fonctionne si vous voulez un ensemble clairsemé (ou répété) de valeurs entières plutôt que d'utiliser les ordinaux par défaut de 0 .. (count-1). Cela peut être important si vous interagissez avec du code existant, par exemple sur un réseau.
benkc

Il convient de souligner que, comme dans les réponses ci-dessus, la mise en cache du résultat de values ​​() vaut probablement la peine, afin d'éviter une allocation de mémoire et une copie de tableau à chaque fois que vous l'invoquez.
benkc

1
Et, selon la longueur de votre énumération, vous souhaiterez peut-être créer un HashMap ou utiliser une recherche binaire ou quelque chose pour cette recherche, plutôt que de faire une recherche linéaire à chaque fois.
benkc

2
Cela devrait être la bonne façon et la meilleure pratique pour convertir int en enum, et je pense que vous pouvez simplifier le problème en statique A GetValue (int _id) {for (A a: A.values ​​() {if (a.getId ( ) == _ id) {return a;}} return null;} Débarrassez-vous des trucs None, isEmpty () et compare ()
Chris.Zou

35

Vous pouvez essayer comme ça.
Créer une classe avec l'ID d'élément.

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

Maintenant, récupérez cette énumération en utilisant id comme int.

MyEnum myEnum = MyEnum.fromId(5);

19

Je mets en cache les valeurs et crée une méthode d'accès statique 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];
    }
}

4
C'est la solution que j'utilise maintenant. Mais à mon humble avis, il est moins déroutant si vous ne donnez pas au champ valuesle même nom que la méthode values(). J'utilise cachedValuespour le nom du champ.
ToolmakerSteve

2
Efficace et élégant; copié et collé dans mon projet :) La seule chose que j'ai changé est le fromInt (int i), que je viens d'appeler depuis (int i) car c'est un peu redondant d'avoir int deux fois dans la signature.
pipedreambomb

1
pourquoi ne pas initialiser depuis le début? pourquoi attendre le premier coup?
kaiser

9

Les énumérations Java n'ont pas le même type de mappage enum-à-int qu'en C ++.

Cela dit, toutes valuesles énumérations ont une méthode qui renvoie un tableau de valeurs d'énumération possibles, donc

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

devrait marcher. C'est un peu méchant et il pourrait être préférable de ne pas essayer de convertir de ints en Enums (ou vice versa) si possible.


9

Ce n'est pas quelque chose qui se fait habituellement, donc je reconsidérerais. Mais cela étant dit, les opérations fondamentales sont: int -> enum en utilisant EnumType.values ​​() [intNum], et enum -> int en utilisant EnumInst.ordinal ().

Cependant, étant donné que toute implémentation de values ​​() n'a pas d'autre choix que de vous donner une copie du tableau (les tableaux java ne sont jamais en lecture seule), vous seriez mieux servi en utilisant un EnumMap pour mettre en cache le mappage enum -> int.


2
Re "Ce n'est pas quelque chose qui se fait habituellement": cas courant où cela est utile: l'énumération correspond aux valeurs int stockées dans une base de données.
ToolmakerSteve

@ToolmakerSteve, vous avez absolument raison de dire que les mappages sont requis. Mais voudriez-vous laisser ce type d'encodage à un mappeur OU ou à une boîte à outils / bibliothèque?
Dilum Ranatunga


6

Voici la solution avec laquelle je prévois aller. Non seulement cela fonctionne avec des entiers non séquentiels, mais cela devrait fonctionner avec tout autre type de données que vous voudrez peut-être utiliser comme id sous-jacent pour vos valeurs d'énumération.

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

Je n'ai besoin de convertir les identifiants en énumérations qu'à des moments spécifiques (lors du chargement de données à partir d'un fichier), il n'y a donc aucune raison pour moi de conserver la carte en mémoire à tout moment. Si vous avez besoin que la carte soit accessible à tout moment, vous pouvez toujours la mettre en cache en tant que membre statique de votre classe Enum.


À mon humble avis, si j'étais préoccupé par l'utilisation de la mémoire, je créerais dynamiquement le HashMap - comme @ossys mais avec un code différent lorsque le cache est nul, puis ajouter une deuxième méthode clearCachedValueslorsque vous avez fini de l'utiliser (qui redéfinit le champ privé sur null). Je considère MyEnum.fromInt(i)plus facile à comprendre que de contourner un objet cartographique.
ToolmakerSteve

6

Au cas où cela aiderait les autres, l'option que je préfère, qui n'est pas répertoriée ici, utilise la fonctionnalité Maps 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);
    }
}

Avec la valeur par défaut que vous pouvez utiliser null, vous pouvez throw IllegalArgumentExceptionou fromIntpouvez retourner un Optional, quel que soit le comportement que vous préférez.


3
Vous devez mentionner que vous utilisez Guava. Ou vous pouvez utiliser des flux:Map<Integer, MyEnum> reverseLookup = Arrays.stream(MyEnum.values()).collect(Collectors.toMap(MyEnum::getValue, Function.identity()));
shmosel

1
Pourrait aussi vouloir définir une getValue()méthode.
shmosel

@shmosel Oups, j'ai raté la fonction getValue, merci. La saisie de versions génériques dans stackoverflow ne se déroule pas toujours. Ajout d'un commentaire sur l'utilisation de Guava avec un lien. Préférez la méthode Guava aux streams.
Chad Befus

4

Vous pouvez parcourir l' values()énumération et comparer la valeur entière de l'énumération avec celle donnée idci-dessous:

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

Et utilisez comme ceci:
TestEnum x = TestEnum.getEnum(4);//Will return TestEnum.Value4
j'espère que cela vous aidera;)


3

Sur la base de la réponse de @ChadBefus et du commentaire de @shmosel, je recommanderais d'utiliser ceci. (Recherche efficace et fonctionne sur java pur> = 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

Une bonne option consiste à éviter la conversion de intà enum: par exemple, si vous avez besoin de la valeur maximale, vous pouvez comparer x.ordinal () à y.ordinal () et renvoyer x ou y en conséquence. (Vous devrez peut-être réorganiser vos valeurs pour que cette comparaison soit significative.)

Si ce n'est pas possible, je stockerais MyEnum.values()dans un tableau statique.


1
Si vous obtenez int de DB et que vous souhaitez le convertir en Enum, ce qui, je pense, est une tâche très courante, vous aurez besoin de int -> conversion d'énumération, IMO ..
Yuki Inoue

0

C'est la même réponse que les médecins, mais elle montre comment éliminer le problème avec les tableaux mutables. Si vous utilisez d'abord ce type d'approche en raison de la prédiction de branche, il aura très peu ou pas d'effet et le code entier n'appellera la fonction de valeurs de tableau mutable () qu'une seule fois. Comme les deux variables sont statiques, elles ne consommeront pas de mémoire n * pour chaque utilisation de cette énumération également.

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 Je pense que les exceptions sont nettement meilleures si les limites sont relativement peu nombreuses.
Zoso

Sur quoi est basée votre croyance?
shmosel

0

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

Usage:

val status = Status.getStatus(1)!!

0

A écrit cette implémentation. Il permet les valeurs manquantes, les valeurs négatives et maintient la cohérence du code. La carte est également mise en cache. Utilise une interface et a besoin de 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;
    }
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.