Ottenere enum associato al valore int


88

In precedenza, i miei enumerazioni LegNo erano definiti semplicemente come:

NO_LEG, LEG_ONE, LEG_TWO

e chiamando return LegNo.values()[i];, sono stato in grado di ottenere il valore associato a ogni enum.

Ma ora ho deciso che voglio che l' LegNoenumerazione NO_LEGsia int -1 invece di 0, quindi ho deciso di utilizzare un costruttore privato per inizializzare e impostare il suo valore int

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

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

l'unica cosa ora è che, poiché lo sto facendo in questo modo, il values()metodo non funzionerà per l' NO_LEGenum. Come ottengo l'enumerazione associata all'int? Esiste un modo efficiente per farlo se non usare un'istruzione switch case o un if-elseif-elseif

Posso vedere molte domande SO relative all'ottenimento del valore int dall'enum, ma cerco il contrario.

Risposte:


146

EDIT Agosto 2018

Oggi lo implementerei come segue

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

Dovrai mantenere una mappatura all'interno dell'enumerazione.

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

Il blocco statico verrà richiamato solo una volta, quindi non ci sono praticamente problemi di prestazioni qui.

EDIT: rinominato il metodo in valueOfquanto è più in linea con altre classi Java.


scusa, non sono sicuro di essere stato abbastanza chiaro. voglio passare nell'int e ottenere l'enum ad esso associato.
L-Samuels

@ L-Samuels Immagino di non aver letto correttamente la tua domanda. Vedi il mio aggiornamento.
adarshr

2
So che questo sembra ovvio, ma lo uso in questo modo: LegNo foo = LegNo.valueOf(2);. Il codice precedente restituirà un file LegNo.LEG_TWO.
FirstOne

1
Da notare, il passaggio di un valore intero non valido (non mappato) restituirà null, come previsto utilizzando HashMap.get : restituisce il valore a cui è mappata la chiave specificata o null se questa mappa non contiene mappatura per la chiave.
FirstOne

Sebbene la sintassi dello stream sia chiara, vale la pena sottolineare che ha una complessità temporale maggiore rispetto alla mappa statica (che ha certamente un consumo di memoria maggiore). Non è un problema per 3 valori, ma sicuramente una preoccupazione se stai valueOf()inserendo un enum di 1000 membri all'interno di un altro ciclo.
Patrick M

24

È anche possibile includere un metodo statico nell'enum che itera tutti i membri e restituisce quello corretto.

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?");
   }
}

Ora, se vuoi ottenere un valore Enum dall'intero, usa semplicemente:

int myLegIndex = 1; //expected : LEG_ONE
LegNo myLeg = LegNo.getLeg(myLegIndex);

Suppongo che questo sarebbe più elegante che usare un'istruzione if else if. Ma dato che c'erano più tentativi di ricerca, la strategia della mappa suggerita da @adarshr sarebbe stata migliore. Anche se vota per l'umorismo.
L-Samuels

1
Mi piace molto anche la strategia della mappa. Soprattutto quando l'enumerazione ha molti valori o deve essere cercata molto spesso tramite questo meccanismo. Tuttavia, se la ricerca dei valori dall'int associato è un evento relativamente raro o si hanno molte enumerazioni diverse con lo stesso requisito di ricerca, credo che la mia strada sarebbe più favorevole alle risorse, poiché l'overhead per la mappa viene salvato. Inoltre, trovo che renda il codice meno disordinato. Tuttavia, ho alcuni casi d'uso in cui passerò sicuramente al tipo di mappa.
Mike Adler

Non dovresti mai derivare un valore enum associato dal suo ordinale. L'utilizzo di una mappa statica È la metodologia consigliata dagli architetti Java.
hfontanez

Il campo legIndex coincide con l'ordinale in questo esempio, ma può essere qualsiasi valore int. Non viene eseguita alcuna ricerca ordinale. Inoltre, fornisci o collega un motivo per cui pensi che la ricerca ordinale non sia valida.
Mike Adler

1
"Gamba non trovata. Amputata?"
Gnagy

17

La risposta di adarshr adattata a 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);
    }
}

11

Puoi anche accedere al valore Enum corrispondente al valore intero dato semplicemente chiamando il metodo values ​​() su enum LegNo. Restituisce il campo delle enumerazioni LegNo: LegNo.values()[0]; //returns LEG_NO LegNo.values()[1]; //returns LEG_ONE LegNo.values()[2]; //returns LEG_TWO

Non proprio la cosa che stava cercando, ma piuttosto vicina e molto semplice in effetti. (Sebbene il soggetto sia morto, potrebbe essere utile per qualcun altro.)


6

Java 8 way con valore predefinito:

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

chiamare:

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

o con eccezione:

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

1

Poiché la tua enumerazione contiene solo 3 elementi, il modo più veloce sarà usare solo una serie di elementi if else, come hai suggerito.

modifica: la risposta fornita da adarshr è più adatta per casi generali, in cui ci sono molti valori enum, ma penso che sia eccessivo per il tuo problema.


Avere un Mapcodice nel codice non è certamente eccessivo. Inoltre rende il metodo molto più pulito di uno spaghetti di se-else.
adarshr

Sono d'accordo che la mappa è migliore una volta che hai molti valori enum, ma per 3 valori mi atterrei a un costrutto if / else. È una questione di gusti immagino.
DieterDP

Qualunque sia l'approccio scelto, la firma del metodo public LegNo valueOf(int value)non deve essere modificata. If-else potrebbe quindi essere scritto all'interno dell'enumerazione stessa. Se if-else esce dall'enumerazione, allora diventa certamente un codice non così pulito.
adarshr

1
Sono completamente d'accordo con te :)
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

4
Accettabile per enumerazioni con valori <10 ma totalmente inefficace per un numero elevato di valori enum a causa della complessità della ricerca 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<>();
  }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.