Converti il ​​valore intero nella corrispondenza di Java Enum


87

Ho un'enumerazione come questa:

public enum PcapLinkType {
  DLT_NULL(0)
  DLT_EN10MB(1)
  DLT_EN3MB(2),
  DLT_AX25(3),
  /*snip, 200 more enums, not always consecutive.*/
  DLT_UNKNOWN(-1);
    private final int value;   

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

Ora ottengo un int dall'input esterno e voglio l'input corrispondente: lanciare un'eccezione se un valore non esiste è ok, ma preferibilmente lo farei DLT_UNKNOWN in quel caso.

int val = in.readInt();
PcapLinkType type = ???; /*convert val to a PcapLinkType */

Risposte:


106

Dovresti farlo manualmente, aggiungendo una mappa statica nella classe che mappa Integer su enumerazioni, come

private static final Map<Integer, PcapLinkType> intToTypeMap = new HashMap<Integer, PcapLinkType>();
static {
    for (PcapLinkType type : PcapLinkType.values()) {
        intToTypeMap.put(type.value, type);
    }
}

public static PcapLinkType fromInt(int i) {
    PcapLinkType type = intToTypeMap.get(Integer.valueOf(i));
    if (type == null) 
        return PcapLinkType.DLT_UNKNOWN;
    return type;
}

1
aggiornato con i consigli di dty, che è stata una buona idea.
MeBigFatGuy

Spero che tu abbia eseguito prima il mio codice attraverso un compilatore ... L'ho appena inventato dalla cima della mia testa. So che la tecnica funziona - l'ho usata ieri. Ma il codice è su un'altra macchina e questa non ha i miei strumenti di sviluppo.
dty

1
allOf è disponibile solo per i set
MeBigFatGuy

1
Inoltre, EnumMapusa le enumerazioni come chiavi. In questo caso, l'OP vuole le enumerazioni come valori.
dty

8
Questo sembra un sacco di spese generali non necessarie. Coloro che effettivamente necessitano di questo tipo di operazione hanno probabilmente bisogno di prestazioni elevate perché stanno scrivendo / leggendo da stream / socket, nel qual caso, la memorizzazione nella cache di values()(se i valori enum sono sequenziali) o una semplice switchistruzione battere questo metodo comodamente . Se hai solo una manciata di voci nel tuo Enum, non ha molto senso aggiungere l'overhead di una HashMap semplicemente per la comodità di non dover aggiornare l' switchistruzione. Questo metodo può sembrare più elegante, ma è anche uno spreco.
schiacciare il

30

C'è un metodo statico values()che è documentato, ma non dove ti aspetteresti: http://docs.oracle.com/javase/tutorial/java/javaOO/enum.html

enum MyEnum {
    FIRST, SECOND, THIRD;
    private static MyEnum[] allValues = values();
    public static MyEnum fromOrdinal(int n) {return allValues[n];}
}

In linea di principio, puoi usare solo values()[i], ma ci sono voci che values()creeranno una copia dell'array ogni volta che viene invocato.


9
Secondo Joshua Bloch (Effective Java Book) : non derivare mai un valore associato a un'enumerazione dal suo ordinale; La tua implementazione non dovrebbe basarsi sull'ordine delle enumerazioni.
stevo.mit

4
Attuazione di cosa? Se implementiamo un algoritmo, l'implementazione non dovrebbe fare affidamento sull'ordine delle enumerazioni a meno che tale ordine non sia documentato. Quando implementiamo l'enumerazione stessa, è consentito utilizzare tali dettagli di implementazione, nello stesso modo in cui è consentito utilizzare metodi privati ​​di classe.
18446744073709551615

1
Non sono d'accordo. Credo che non si intenda mai indipendentemente dalla documentazione. Non dovresti usare gli ordinali anche quando implementi l'enumerazione tu stesso. È un cattivo odore ed è soggetto a errori. Non sono un esperto ma non discuterei con Joshua Bloch :)
stevo.mit

4
@ stevo.mit dai un'occhiata alla nuova enum java.time.Month in Java 8. Il metodo statico Month.of (int) fa esattamente quello che Joshua Bloch ha detto che non dovresti "mai" fare. Restituisce un mese in base al suo ordinale.
Klitos Kyriacou

1
@ stevo.mit Ci sono enumerazioni ordinate e enumerazioni non ordinate . (E anche la maschera di bit enum .) È semplicemente scorretto parlarne solo come "enumerazioni". La decisione su quale mezzo espressivo utilizzare deve essere basata sul livello di astrazione su cui lavori. Non è infatti corretto utilizzare dettagli di implementazione (mezzi espressivi del livello inferiore) o ipotesi di utilizzo (mezzi espressivi del livello superiore). Quanto a " mai ", nelle lingue umane mai mai significa mai, perché c'è sempre un contesto. (Di solito, nella programmazione dell'applicazione, mai ...) BTW, programering.com/a/MzNxQjMwATM.html
18446744073709551615

16

Dovrai creare un nuovo metodo statico in cui iterare PcapLinkType.values ​​() e confrontare:

public static PcapLinkType forCode(int code) {
    for (PcapLinkType typе : PcapLinkType.values()) {
        if (type.getValue() == code) {
            return type;
        }
    }
    return null;
 }

Andrebbe bene se viene chiamato raramente. Se viene chiamato frequentemente, guarda l' Mapottimizzazione suggerita da altri.


4
Potrebbe essere costoso se chiamato molto. È probabile che la costruzione di una mappa statica dia un costo ammortizzato migliore.
dty

@dty o (n) with n = 200 - non penso sia un problema
Bozho

7
Questa è un'affermazione totalmente ridicola senza la sensazione di quanto spesso viene chiamata. Se viene chiamato una volta, va bene. Se viene chiamato per ogni pacchetto che sfreccia su una rete 10Ge, è molto importante rendere un algoritmo 200 volte più veloce. Ecco perché ho qualificato la mia affermazione con "se chiamato molto"
dty

10

Puoi fare qualcosa del genere per registrarli automaticamente tutti in una raccolta con la quale convertire facilmente gli interi nell'enumerazione corrispondente. (A proposito, non è consentito aggiungerli alla mappa nel costruttore enum . È bello imparare cose nuove anche dopo molti anni di utilizzo di Java. :)

public enum PcapLinkType {
    DLT_NULL(0),
    DLT_EN10MB(1),
    DLT_EN3MB(2),
    DLT_AX25(3),
    /*snip, 200 more enums, not always consecutive.*/
    DLT_UNKNOWN(-1);

    private static final Map<Integer, PcapLinkType> typesByValue = new HashMap<Integer, PcapLinkType>();

    static {
        for (PcapLinkType type : PcapLinkType.values()) {
            typesByValue.put(type.value, type);
        }
    }

    private final int value;

    private PcapLinkType(int value) {
        this.value = value;
    }

    public static PcapLinkType forValue(int value) {
        return typesByValue.get(value);
    }
}

1
Questo è ciò che ottieni ricontrollando la tua risposta prima di pubblicare. ;)
Esko Luontola

10

se hai enum come questo

public enum PcapLinkType {
  DLT_NULL(0)
  DLT_EN10MB(1)
  DLT_EN3MB(2),
  DLT_AX25(3),
  DLT_UNKNOWN(-1);

    private final int value;   

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

allora puoi usarlo come

PcapLinkType type = PcapLinkType.values()[1]; /*convert val to a PcapLinkType */

ti sei perso il commento / * snip, 200 enumerazioni in più, non sempre consecutive. * /
MeBigFatGuy

solo nel caso in cui il tuo valore enum sia transitività da zero, questa è una cattiva pratica
cuasodayleo

4

Come dice @MeBigFatGuy, tranne per il fatto che puoi fare in modo che il tuo static {...}blocco utilizzi un loop sulla values()raccolta:

static {
    for (PcapLinkType type : PcapLinkType.values()) {
        intToTypeMap.put(type.getValue(), type);
    }
}

4

So che questa domanda è vecchia di alcuni anni, ma poiché Java 8, nel frattempo, ci ha portato Optional, ho pensato di offrire una soluzione utilizzandolo (e Streame Collectors):

public enum PcapLinkType {
  DLT_NULL(0),
  DLT_EN3MB(2),
  DLT_AX25(3),
  /*snip, 200 more enums, not always consecutive.*/
  // DLT_UNKNOWN(-1); // <--- NO LONGER NEEDED

  private final int value;
  private PcapLinkType(int value) { this.value = value; }

  private static final Map<Integer, PcapLinkType> map;
  static {
    map = Arrays.stream(values())
        .collect(Collectors.toMap(e -> e.value, e -> e));
  }

  public static Optional<PcapLinkType> fromInt(int value) {
    return Optional.ofNullable(map.get(value));
  }
}

Optionalè come null: rappresenta un caso in cui non c'è un valore (valido). Ma è un'alternativa più indipendente dai tipi nullo un valore predefinito, ad esempio DLT_UNKNOWNperché potresti dimenticare di controllare i casi nullo DLT_UNKNOWN. Sono entrambi PcapLinkTypevalori validi ! Al contrario, non è possibile assegnare un Optional<PcapLinkType>valore a una variabile di tipo PcapLinkType. Optionalti fa verificare prima un valore valido.

Ovviamente, se vuoi conservare DLT_UNKNOWNper compatibilità con le versioni precedenti o qualsiasi altra ragione, puoi comunque usare Optionalanche in quel caso, usando orElse()per specificarlo come valore predefinito:

public enum PcapLinkType {
  DLT_NULL(0),
  DLT_EN3MB(2),
  DLT_AX25(3),
  /*snip, 200 more enums, not always consecutive.*/
  DLT_UNKNOWN(-1);

  private final int value;
  private PcapLinkType(int value) { this.value = value; }

  private static final Map<Integer, PcapLinkType> map;
  static {
    map = Arrays.stream(values())
        .collect(Collectors.toMap(e -> e.value, e -> e));
  }

  public static PcapLinkType fromInt(int value) {
    return Optional.ofNullable(map.get(value)).orElse(DLT_UNKNOWN);
  }
}

3

È possibile aggiungere un metodo statico nella propria enumerazione che accetta un intcome parametro e restituisce un file PcapLinkType.

public static PcapLinkType of(int linkType) {

    switch (linkType) {
        case -1: return DLT_UNKNOWN
        case 0: return DLT_NULL;

        //ETC....

        default: return null;

    }
}

Meglio non dimenticare di aggiungere una voce a quella switchdichiarazione se aggiungi una nuova enum. Non è l'ideale, IMHO.
dty

1
@dty Quindi, pensi che il sovraccarico di una HashMap superi la necessità di aggiungere un nuovo caso a un'istruzione switch?
schiacciare il

1
Penso che preferirei scrivere codice che mi aiuti a non commettere errori ed è quindi più probabile che sia corretto prima di concentrarmi sulle micro-prestazioni di una ricerca hash.
dty

3

Questo è quello che uso:

public enum Quality {ENOUGH,BETTER,BEST;
                     private static final int amount = EnumSet.allOf(Quality.class).size();
                     private static Quality[] val = new Quality[amount];
                     static{ for(Quality q:EnumSet.allOf(Quality.class)){ val[q.ordinal()]=q; } }
                     public static Quality fromInt(int i) { return val[i]; }
                     public Quality next() { return fromInt((ordinal()+1)%amount); }
                    }

L'uso di ordinal è stato identificato come una cattiva pratica, in generale, è meglio evitare.
Rafael

1
static final PcapLinkType[] values  = { DLT_NULL, DLT_EN10MB, DLT_EN3MB, null ...}    

...

public static PcapLinkType  getPcapLinkTypeForInt(int num){    
    try{    
       return values[int];    
    }catch(ArrayIndexOutOfBoundsException e){    
       return DLT_UKNOWN;    
    }    
}    

1
Costoso se chiamato molto. È necessario ricordarsi di aggiornare l'array (perché ce l'hai anche quando le enumerazioni definiscono un .values()metodo?).
dty

@dty è il tentativo / cattura? Penso che sarebbe più giusto dire che è costoso se molti dei valori rientrano nella categoria DLT_UNKNOWN.
nsfyn55

1
Sono davvero sorpreso di vedere una soluzione di matrice rifiutata e una soluzione di mappa votata a favore. Quello che non mi piace qui è --int, ma ovviamente è un errore di battitura.
18446744073709551615

Vedo: vogliono nullal posto di DLT_UKNOWN:)
18446744073709551615

1
Perché no static final values[] = PcapLinkType.values()?
18446744073709551615

0

Non è possibile gestire in modo elegante i tipi enumerati basati su numeri interi. Potresti pensare di utilizzare un'enumerazione basata su stringhe invece della tua soluzione. Non è sempre un modo preferito, ma esiste ancora.

public enum Port {
  /**
   * The default port for the push server.
   */
  DEFAULT("443"),

  /**
   * The alternative port that can be used to bypass firewall checks
   * made to the default <i>HTTPS</i> port.
   */
  ALTERNATIVE("2197");

  private final String portString;

  Port(final String portString) {
    this.portString = portString;
  }

  /**
   * Returns the port for given {@link Port} enumeration value.
   * @return The port of the push server host.
   */
  public Integer toInteger() {
    return Integer.parseInt(portString);
  }
}
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.