Conversion de l'énumération ordinale en type énumération

316

J'ai le type d'énumération ReportTypeEnum qui est transmis entre les méthodes dans toutes mes classes, mais je dois ensuite le passer sur l'URL, donc j'utilise la méthode ordinale pour obtenir la valeur int. Après l'avoir obtenu dans mon autre page JSP, je dois le reconvertir en un ReportTypeEnumafin de pouvoir continuer à le transmettre.

Comment puis-je convertir l'ordinal en ReportTypeEnum ?

Utilisation de Java 6 SE.

Lennie
la source
1
Il n'y a pas de Java 6 EE, jusqu'à présent (AFAIK). Il y a Java SE 6 et Java EE 5.
Hosam Aly

Réponses:

632

Pour convertir un ordinal en sa représentation enum, vous pouvez procéder comme suit:

ReportTypeEnum value = ReportTypeEnum.values()[ordinal];

Veuillez noter les limites du tableau.

Notez que chaque appel à values()renvoie un tableau nouvellement cloné, ce qui peut avoir un impact négatif sur les performances. Vous souhaiterez peut-être mettre en cache le tableau s'il doit être appelé souvent.

Exemple de code sur la façon de mettre en cachevalues() .


Cette réponse a été modifiée pour inclure les commentaires fournis dans les commentaires

Joachim Sauer
la source
J'ai implémenté cette solution et cela ne fonctionne pas pour moi. Il renvoie la valeur ordinale n'est pas garantie pour correspondre à l'ordre dans lequel les types énumérés sont ajoutés. Je ne sais pas ce que cette réponse préconise, mais je voulais quand même avertir les gens
IcedDante
@IcesDante: l'ordinal est certainement garanti pour correspondre à l'ordre des valeurs d'énumération dans la source. Si vous observez un comportement différent, alors quelque chose d'autre doit être faux. Ma réponse ci-dessus est cependant sous-optimale pour toutes les raisons exposées dans les autres réponses.
Joachim Sauer
@JoachimSauer Peut-être que IcedDante signifie que l'ordinal peut ne pas correspondre s'il a été produit et enregistré par une version antérieure de la source, dans laquelle les valeurs d'énumération étaient dans un ordre différent.
LarsH Il y a
137

C'est presque certainement une mauvaise idée . Certes, si l'ordinal est de facto persistant (par exemple, parce que quelqu'un a mis en signet l'URL) - cela signifie que vous devez toujours conserver l' enumordre à l'avenir, ce qui peut ne pas être évident pour les responsables du code sur la ligne.

Pourquoi ne pas encoder l' enumutilisation myEnumValue.name()(et décoder via ReportTypeEnum.valueOf(s)) à la place?

oxbow_lakes
la source
24
Et si vous changez le nom de l'énumération (mais conservez la commande)?
Arne Evertsson
6
@Arne - Je pense que cela est beaucoup moins probable qu'une personne inexpérimentée qui vient et ajoute un valueau début ou à sa position alphabétique / logique correcte. (Par logique, je veux dire par exemple que les TimeUnitvaleurs ont une position logique)
oxbow_lakes
7
Je préfère certainement forcer l'ordre des énumérations plutôt que le nom de mon énumération ... c'est pourquoi je préfère stocker l'ordinal plutôt que le nom de l'énumération dans la base de données. De plus, il vaut mieux utiliser la manipulation int plutôt que String ...
8
Je suis d'accord. Dans une API publique, la modification du nom d'une énumération romprait la compatibilité descendante, mais pas la modification de l'ordre. Pour cette raison, il est plus logique d'utiliser le nom comme votre "clé"
Noel
3
Le stockage de l'ordinal facilite la traduction de vos idées dans d'autres langues. Et si vous avez besoin d'écrire un composant en C?
QED
94

Si je vais utiliser values()beaucoup:

enum Suit {
   Hearts, Diamonds, Spades, Clubs;
   public static final Suit values[] = values();
}

Pendant ce temps où.java:

Suit suit = Suit.values[ordinal];

Faites attention aux limites de votre tableau.

QED
la source
3
+1 c'est de loin la meilleure solution à mon humble avis car on peut faire circuler des ordinaux surtout dans android.os.Message.
likejudo
9
C'est très inquiétant car les tableaux sont modifiables . Même si les valeurs [] sont finales, cela n'empêche pas Suit.values[0] = Suit.Diamonds;quelque part dans votre code. Idéalement, cela ne se produira jamais, mais le principe général de ne pas exposer les champs mutables est toujours valable. Pour cette approche, envisagez d'utiliser Collections.unmodifiableListou similaire à la place.
Mshnik
Que diriez-vous - privé final statique Valeurs Suit [] = values ​​(); public static Suit [] getValues ​​() {return values; }
Pratyush
4
@Pratyush qui rendrait la variable tableau immuable, mais pas son contenu. Je pourrais toujours faire getValues ​​() [0] = somethingElse;
Calabacin
D'accord, donc le point n'est pas d'exposer Suit values[]directement ou indirectement (comment cela a été mentionné getValues()), je travaille avec une méthode publique où la ordinalvaleur doit être envoyée comment un argument et renvoyer la Suitreprésentation Suit values[]. Le point ici (la tuile de la question depuis le début) était de créer un type d'énumération à partir d'un ordinal d'énumération
Manuel Jordan
13

Je suis d'accord avec la plupart des gens que l'utilisation de l'ordinal est probablement une mauvaise idée. Je résout généralement ce problème en donnant à l'énumération un constructeur privé qui peut prendre par exemple une valeur DB, puis créer une fromDbValuefonction statique similaire à celle de la réponse de Jan.

public enum ReportTypeEnum {
    R1(1),
    R2(2),
    R3(3),
    R4(4),
    R5(5),
    R6(6),
    R7(7),
    R8(8);

    private static Logger log = LoggerFactory.getLogger(ReportEnumType.class);  
    private static Map<Integer, ReportTypeEnum> lookup;
    private Integer dbValue;

    private ReportTypeEnum(Integer dbValue) {
        this.dbValue = dbValue;
    }


    static {
        try {
            ReportTypeEnum[] vals = ReportTypeEnum.values();
            lookup = new HashMap<Integer, ReportTypeEnum>(vals.length);

            for (ReportTypeEnum  rpt: vals)
                lookup.put(rpt.getDbValue(), rpt);
         }
         catch (Exception e) {
             // Careful, if any exception is thrown out of a static block, the class
             // won't be initialized
             log.error("Unexpected exception initializing " + ReportTypeEnum.class, e);
         }
    }

    public static ReportTypeEnum fromDbValue(Integer dbValue) {
        return lookup.get(dbValue);
    }

    public Integer getDbValue() {
        return this.dbValue;
    }

}

Vous pouvez maintenant modifier l'ordre sans modifier la recherche et vice versa.

jmkelm08
la source
CECI est la bonne réponse. Je suis surpris qu'il ait obtenu si peu de points par rapport à d'autres réponses plus directes mais potentiellement invalides (en raison de modifications de code à l'avenir).
Calabacin
8

Vous pouvez utiliser une table de recherche statique:

public enum Suit {
  spades, hearts, diamonds, clubs;

  private static final Map<Integer, Suit> lookup = new HashMap<Integer, Suit>();

  static{
    int ordinal = 0;
    for (Suit suit : EnumSet.allOf(Suit.class)) {
      lookup.put(ordinal, suit);
      ordinal+= 1;
    }
  }

  public Suit fromOrdinal(int ordinal) {
    return lookup.get(ordinal);
  }
}
Jan
la source
3
Voir aussi Enums .
trashgod
11
Hou la la! Juste wow! C'est bien sûr, bien sûr, mais ... vous savez - le programmeur C en moi hurle de douleur en voyant que vous allouez un HashMap à part entière et effectuez des recherches à l'intérieur de tout JUSTE pour gérer essentiellement 4 constantes: pique, cœur, diamants et clubs! Le programmeur AC allouerait 1 octet pour chacun: 'const char CLUBS = 0;' etc ... Oui, une recherche HashMap est O (1), mais la surcharge de mémoire et de CPU d'un HashMap, dans ce cas, rend les ordres de grandeur plus lents et gourmands en ressources que d'appeler directement .values ​​()! Pas étonnant que Java soit un tel porc de mémoire si les gens écrivent comme ça ...
Leszek
2
Tous les programmes ne nécessitent pas la performance d'un jeu triple A. Dans de nombreux cas, l'échange de mémoire et de CPU pour la sécurité, la lisibilité, la maintenabilité, la prise en charge multiplateforme, la récupération de place, etc. est justifiable. Les langues de niveau supérieur existent pour une raison.
janvier
3
Mais si votre plage de clés est toujours 0...(n-1), alors un tableau est moins de code et plus lisible aussi; l'amélioration des performances n'est qu'un bonus. private static final Suit[] VALUES = values();et public Suit fromOrdinal(int ordinal) { return VALUES[ordinal]; }. Avantage supplémentaire: se bloque immédiatement sur les ordinaux invalides, plutôt que de retourner silencieusement null. (Pas toujours un avantage. Mais souvent.)
Thomas
4

C'est ce que j'utilise. Je ne prétends pas que c'est beaucoup moins "efficace" que les solutions plus simples ci-dessus. Ce qu'il fait est de fournir un message d'exception beaucoup plus clair que "ArrayIndexOutOfBounds" lorsqu'une valeur ordinale non valide est utilisée dans la solution ci-dessus.

Il utilise le fait que javadoc EnumSet spécifie que l'itérateur renvoie les éléments dans leur ordre naturel. Il y a une affirmation si ce n'est pas correct.

Le test JUnit4 montre comment il est utilisé.

 /**
 * convert ordinal to Enum
 * @param clzz may not be null
 * @param ordinal
 * @return e with e.ordinal( ) == ordinal
 * @throws IllegalArgumentException if ordinal out of range
 */
public static <E extends Enum<E> > E lookupEnum(Class<E> clzz, int ordinal) {
    EnumSet<E> set = EnumSet.allOf(clzz);
    if (ordinal < set.size()) {
        Iterator<E> iter = set.iterator();
        for (int i = 0; i < ordinal; i++) {
            iter.next();
        }
        E rval = iter.next();
        assert(rval.ordinal() == ordinal);
        return rval;
    }
    throw new IllegalArgumentException("Invalid value " + ordinal + " for " + clzz.getName( ) + ", must be < " + set.size());
}

@Test
public void lookupTest( ) {
    java.util.concurrent.TimeUnit tu = lookupEnum(TimeUnit.class, 3);
    System.out.println(tu);
}
gerardw
la source
1

Voici ce que je fais sur Android avec Proguard:

public enum SomeStatus {
    UNINITIALIZED, STATUS_1, RESERVED_1, STATUS_2, RESERVED_2, STATUS_3;//do not change order

    private static SomeStatus[] values = null;
    public static SomeStatus fromInteger(int i) {
        if(SomeStatus.values == null) {
            SomeStatus.values = SomeStatus.values();
        }
        if (i < 0) return SomeStatus.values[0];
        if (i >= SomeStatus.values.length) return SomeStatus.values[0];
        return SomeStatus.values[i];
    }
}

c'est court et je n'ai pas besoin de m'inquiéter d'avoir une exception dans Proguard

Quelqu'un quelque part
la source
1

Vous pouvez définir une méthode simple comme:

public enum Alphabet{
    A,B,C,D;

    public static Alphabet get(int index){
        return Alphabet.values()[index];
    }
}

Et utilisez-le comme:

System.out.println(Alphabet.get(2));
Amir Fo
la source
0
public enum Suit implements java.io.Serializable, Comparable<Suit>{
  spades, hearts, diamonds, clubs;
  private static final Suit [] lookup  = Suit.values();
  public Suit fromOrdinal(int ordinal) {
    if(ordinal< 1 || ordinal> 3) return null;
    return lookup[value-1];
  }
}

la classe de test

public class MainTest {
    public static void main(String[] args) {
        Suit d3 = Suit.diamonds;
        Suit d3Test = Suit.fromOrdinal(2);
        if(d3.equals(d3Test)){
            System.out.println("Susses");
        }else System.out.println("Fails");
    }
}

J'apprécie que vous partagiez avec nous si vous avez un code plus efficace, mon énumération est énorme et constamment appelée des milliers de fois.

Ar maj
la source
Je pense que vous vouliez dire "if (ordinal <1 || ordinal> 4) return null;"
geowar
0

Donc, une façon est de faire ExampleEnum valueOfOrdinal = ExampleEnum.values()[ordinal];ce qui fonctionne et c'est facile, cependant, comme mentionné précédemment, ExampleEnum.values()retourne un nouveau tableau cloné pour chaque appel. Cela peut être inutilement coûteux. Nous pouvons résoudre ce problème en mettant le tableau en cache de cette façon ExampleEnum[] values = values(). Il est également "dangereux" de permettre à notre tableau mis en cache d'être modifié. Quelqu'un pourrait écrire ExampleEnum.values[0] = ExampleEnum.type2;Donc je le rendrais privé avec une méthode d'accesseur qui ne fait pas de copie supplémentaire.

private enum ExampleEnum{
    type0, type1, type2, type3;
    private static final ExampleEnum[] values = values();
    public static ExampleEnum value(int ord) {
        return values[ord];
    }
}

Vous utiliseriez ExampleEnum.value(ordinal)pour obtenir la valeur d'énumération associée àordinal

joe pelletier
la source
-1

Chaque enum a un nom (), qui donne une chaîne avec le nom du membre enum.

Étant donné enum Suit{Heart, Spade, Club, Diamond}, Suit.Heart.name()donneraHeart .

Chaque énumération a un valueOf() méthode, qui prend un type d'énumération et une chaîne, pour effectuer l'opération inverse:

Enum.valueOf(Suit.class, "Heart")retourne Suit.Heart.

Pourquoi quelqu'un utiliserait des ordinaux me dépasse. Cela peut être plus rapide de nanosecondes, mais ce n'est pas sûr, si les membres de l'énumération changent, car un autre développeur peut ne pas être au courant qu'un code s'appuie sur des valeurs ordinales (en particulier dans la page JSP citée dans la question, la surcharge du réseau et de la base de données domine complètement le temps, sans utiliser un entier sur une chaîne).

Tony BenBrahim
la source
2
Parce que comparer des entiers est beaucoup plus rapide que comparer des chaînes?
HighCommander4
2
Mais les ordinaux changeront si quelqu'un modifie l'énumération (ajoute / réorganise les memvers). Parfois, il s'agit de sécurité et non de vitesse, en particulier sur une page JSP où la latence du réseau est à 1 000 000 fois la différence entre la comparaison d'un tableau d'entiers (une chaîne) et un seul entier.
Tony BenBrahim
toString peut être remplacé, il peut donc ne pas retourner le nom de l'énumération. Le nom de la méthode () est ce qui donne le nom de l'énumération (c'est définitif)
gerardw
les commentaires de code et les versions d'application sont également une chose (peut-être qu'un format de fichier est plus simple et plus petit de cette façon)
joe pelletier
Je ne sais pas pourquoi cela a été rétrogradé alors qu'il recommande essentiellement la même chose que dans la réponse d'oxbow_lakes. Certainement plus sûr que d'utiliser l'ordinal.