Obtenir enum associé à la valeur int

88

Auparavant, j'avais mes énumérations LegNo définies simplement comme:

NO_LEG, LEG_ONE, LEG_TWO

et en appelant return LegNo.values()[i];, j'ai pu obtenir la valeur associée à chaque enum.

Mais maintenant, j'ai décidé que je veux que l' LegNoénumération NO_LEGsoit le int -1 au lieu de 0, alors j'ai décidé d'utiliser un constructeur privé pour initialiser et définir sa valeur int

NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

private LegNo(final int leg) { legNo = leg; }

la seule chose maintenant est que parce que je le fais de cette façon, la values()méthode ne fonctionnera pas pour l' NO_LEGénumération. Comment puis-je obtenir l'énumération associée à l'int? Existe-t-il un moyen efficace de faire cela autre que d'utiliser une instruction case switch ou un if-elseif-elseif

Je peux voir beaucoup de questions SO liées à l'obtention de la valeur int à partir de l'énumération, mais je suis après l'inverse.

L-Samuels
la source

Réponses:

147

EDIT août 2018

Aujourd'hui, je mettrais en œuvre cela comme suit

public enum LegNo {
    NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

    private final int value;

    LegNo(int value) {
        this.value = value;
    }

    public static Optional<LegNo> valueOf(int value) {
        return Arrays.stream(values())
            .filter(legNo -> legNo.value == value)
            .findFirst();
    }
}

Vous devrez maintenir un mappage à l'intérieur de l'énumération.

public enum LegNo {
    NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

    private int legNo;

    private static Map<Integer, LegNo> map = new HashMap<Integer, LegNo>();

    static {
        for (LegNo legEnum : LegNo.values()) {
            map.put(legEnum.legNo, legEnum);
        }
    }

    private LegNo(final int leg) { legNo = leg; }

    public static LegNo valueOf(int legNo) {
        return map.get(legNo);
    }
}

Le bloc statique ne sera appelé qu'une seule fois, il n'y a donc pratiquement aucun problème de performances ici.

EDIT: renommé la méthode en valueOfcar elle est plus en ligne avec d'autres classes Java.

Adarshr
la source
désolé je ne sais pas si j'ai été assez clair. Je veux passer l'int et obtenir l'énumération qui lui est associée.
L-Samuels
@ L-Samuels Je suppose que je n'ai pas bien lu votre question. Voir ma mise à jour.
adarshr
2
Je sais que cela semble évident, mais l' utiliser comme ceci: LegNo foo = LegNo.valueOf(2);. Le code précédent renverra un LegNo.LEG_TWO.
FirstOne
1
À noter, la transmission d'une valeur entière non valide (non mappée) retournera null, comme prévu en utilisant HashMap.get : Renvoie la valeur à laquelle la clé spécifiée est mappée, ou null si cette carte ne contient aucun mappage pour la clé.
FirstOne
Bien que la syntaxe du flux soit soignée, il convient de souligner qu'elle a une complexité temporelle plus élevée que la carte statique (qui a certes une consommation de mémoire plus élevée). Ce n'est pas un problème pour 3 valeurs, mais certainement un problème si vous êtes valueOf()une énumération de 1000 membres dans une autre boucle.
Patrick M
24

Vous pouvez également inclure une méthode statique dans l'énumération qui itère sur tous les membres et renvoie la méthode correcte.

public enum LegNo {
   NO_LEG(-1),
   LEG_ONE(1),
   LEG_TWO(2);

   private int legIndex;

   private LegNo(int legIndex) { this.legIndex = legIndex; }

   public static LegNo getLeg(int legIndex) {
      for (LegNo l : LegNo.values()) {
          if (l.legIndex == legIndex) return l;
      }
      throw new IllegalArgumentException("Leg not found. Amputated?");
   }
}

Maintenant, si vous voulez obtenir une valeur Enum par l'entier, il vous suffit d'utiliser:

int myLegIndex = 1; //expected : LEG_ONE
LegNo myLeg = LegNo.getLeg(myLegIndex);
Mike Adler
la source
Je suppose que ce serait plus élégant que d'utiliser une instruction if else if. Mais étant donné qu'il y avait plus d'énumérations à parcourir, la stratégie de carte suggérée par @adarshr serait meilleure. Bien voter pour l'humour.
L-Samuels
1
J'aime beaucoup la stratégie de la carte aussi. Surtout quand l'énumération a beaucoup de valeurs ou qu'elle doit être recherchée très souvent via ce mécanisme. Cependant, si la recherche de valeurs par l'int associé est une occurrence relativement rare ou si vous avez beaucoup d'énumérations différentes avec la même exigence de recherche, je pense que ma méthode serait plus conviviale pour les ressources, car la surcharge de la carte est enregistrée. De plus, je trouve que cela rend le code moins encombré. J'ai cependant quelques cas d'utilisation dans lesquels je vais certainement passer moi-même au type de carte.
Mike Adler
Vous ne devez jamais dériver une valeur d'énumération associée par son ordinal. L'utilisation d'une carte statique EST la méthodologie recommandée par les architectes Java.
hfontanez
Le champ legIndex coïncide avec l'ordinal dans cet exemple, mais peut être n'importe quelle valeur int. Aucune recherche ordinale n'est effectuée. Veuillez également indiquer ou lier une raison pour laquelle vous pensez que la recherche ordinale est mauvaise.
Mike Adler
1
"Jambe introuvable. Amputée?"
Gnagy
17

La réponse d'adarshr adaptée à Java 8:

import static java.util.Arrays.stream;
import static java.util.stream.Collectors.toMap;

import java.util.Map;

public enum LegNo {
    NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

    private final int legNo;

    private final static Map<Integer, LegNo> map =
            stream(LegNo.values()).collect(toMap(leg -> leg.legNo, leg -> leg));

    private LegNo(final int leg) {
        legNo = leg;
    }

    public static LegNo valueOf(int legNo) {
        return map.get(legNo);
    }
}
Marcin
la source
11

Vous pouvez également accéder à la valeur Enum correspondant à une valeur entière donnée simplement en appelant la méthode values ​​() sur enum LegNo. Il renvoie le champ d'énumérations LegNo: LegNo.values()[0]; //returns LEG_NO LegNo.values()[1]; //returns LEG_ONE LegNo.values()[2]; //returns LEG_TWO

Pas exactement ce qu'il cherchait, mais assez proche et très simple en effet. (Bien que le sujet soit mort, il pourrait être utile pour quelqu'un d'autre.)

Tadeas
la source
6

Java 8 façon avec valeur par défaut:

public enum LegNo {
    NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

    private final int legNo;

    LegNo(int legNo) {
        this.legNo = legNo;
    }

    public static LegNo find(int legNo, Supplier<? extends LegNo> byDef) {
        return Arrays.asList(LegNo.values()).stream()
                .filter(e -> e.legNo == legNo).findFirst().orElseGet(byDef);
    }
}

appeler:

LegNo res = LegNo.find(0, () -> LegNo.NO_LEG);

ou avec exception:

LegNo res = LegNo.find(0, () -> {
    throw new RuntimeException("No found");
});
Dmitry Sokolyuk
la source
1

Étant donné que votre énumération ne contient que 3 éléments, le moyen le plus rapide sera d'utiliser simplement une série de if else, comme vous l'avez suggéré.

edit: la réponse fournie par adarshr est mieux adaptée aux cas généraux, où il existe de nombreuses valeurs d'énumération, mais je pense que c'est exagéré pour votre problème.

DieterDP
la source
Avoir un Mapdans votre code n'est certainement pas exagéré. De plus, cela rend la méthode beaucoup plus propre qu'un spaghetti de conditions if-else.
adarshr
Je suis d'accord que la carte est meilleure une fois que vous avez beaucoup de valeurs d'énumération, mais pour 3 valeurs, je m'en tiendrai à une construction if / else. C'est une question de goût je suppose.
DieterDP
Quelle que soit l'approche choisie, la signature de la méthode public LegNo valueOf(int value)ne doit pas être modifiée. Le if-else pourrait alors être écrit dans l'énumération elle-même. Si le if-else sort de l'énumération, alors il devient certainement un code pas si propre.
adarshr
1
Je suis tout à fait d'accord avec vous :)
DieterDP
1
public enum LegNo {
    NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

    private int legNo;

    private LegNo(int leg) { legNo = leg; }

    public static LegNo valueOf(int legNo) {
        for (LegNo leg : LegNo.values()) {
            if (leg.legNo == legNo) return leg;
        }   
    }
}

assert LegNo.valueOf(2) == LegNo.LEG_TWO
assert LegNo.valueOf(3) == null
Tom B
la source
4
Acceptable pour les énumérations avec des valeurs <10 mais totalement inefficace pour un grand nombre de valeurs enum en raison de la complexité de la recherche O (n)
Alfishe
1
public enum LegNo {

  NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

  private final int code;

  LegNo(int code) {
    this.code = code;
    ReverseStorage.reverseMap.put(code, this);
  }

  public static Optional<LegNo> getByCode(int code) {
    return Optional.ofNullable(ReverseStorage.reverseMap.get(code));
  }

  private static final class ReverseStorage {
    private static final Map<Integer, LegNo> reverseMap = new LinkedHashMap<>();
  }
}
Andrey Lebedenko
la source