Converti da enum ordinal a enum type


316

Ho il tipo enum ReportTypeEnumche viene passato tra i metodi in tutte le mie classi, ma poi devo passare questo sull'URL, quindi uso il metodo ordinale per ottenere il valore int. Dopo averlo visualizzato nell'altra mia pagina JSP, devo convertirlo in un altro in ReportTypeEnummodo da poter continuare a passarlo.

Come posso convertire ordinale in ReportTypeEnum?

Utilizzo di Java 6 SE.


1
Fino ad ora non esiste Java 6 EE (AFAIK). C'è Java SE 6 e Java EE 5.
Hosam Aly

Risposte:


632

Per convertire un ordinale nella sua rappresentazione enum potresti voler fare questo:

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

Notare i limiti dell'array.

Si noti che ogni chiamata a values()restituisce un array appena clonato che potrebbe influire negativamente sulle prestazioni. È possibile che si desideri memorizzare nella cache l'array se verrà chiamato spesso.

Esempio di codice su come memorizzare nella cachevalues() .


Questa risposta è stata modificata per includere il feedback fornito all'interno dei commenti


Ho implementato questa soluzione e non funziona per me. Restituisce che il valore ordinale non è garantito per corrispondere all'ordine in cui vengono aggiunti i tipi elencati. Non so che sia ciò che questa risposta sta sostenendo, ma volevo comunque avvisare le persone
IcedDante

@IcesDante: l'ordinale è sicuramente garantito per corrispondere all'ordine dei valori di enumerazione nella fonte. Se osservi un comportamento diverso, allora qualcos'altro deve essere sbagliato. La mia risposta sopra è tuttavia non ottimale per tutti i motivi indicati nelle altre risposte.
Joachim Sauer,

@JoachimSauer Forse IcedDante significa che l'ordinale potrebbe non corrispondere se fosse prodotto e salvato da una versione precedente della fonte, in cui i valori di enum erano in un ordine diverso.
LarsH

137

Questa è quasi certamente una cattiva idea . Certamente se l'ordinale è di fatto persistente (ad esempio perché qualcuno ha aggiunto l'URL ai segnalibri), significa che enumin futuro dovrai sempre conservare l' ordinamento, il che potrebbe non essere ovvio per i manutentori del codice lungo la linea.

Perché non codificare invece l' enumutilizzo myEnumValue.name()(e la decodifica tramite ReportTypeEnum.valueOf(s))?


24
Cosa succede se si modifica il nome dell'enum (ma si mantiene l'ordinamento)?
Arne Evertsson,

6
@Arne - Penso che questo sia molto meno probabile di una persona inesperta che arriva e aggiunge un valueall'inizio o la sua corretta posizione alfabetica / logica. (Per logica intendo ad esempio che i TimeUnitvalori hanno una posizione logica)
oxbow_lakes,

7
Preferisco certamente forzare l'ordine degli enum piuttosto che il nome del mio enum ... ecco perché preferisco archiviare l'ordinale piuttosto che il nome dell'enum nel database. Inoltre, è meglio usare la manipolazione int piuttosto che String ...

8
Sono d'accordo. In un'API pubblica, la modifica del nome di un Enum interromperebbe la retrocompatibilità, ma la modifica dell'ordine no. Per questa ragione, ha più senso usare il nome come "chiave"
Noel,

3
La memorizzazione dell'ordinale semplifica la traduzione delle idee in altre lingue. Cosa succede se è necessario scrivere un componente in C?
QED

94

Se userò values()molto:

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

Nel frattempo ovunque.java:

Suit suit = Suit.values[ordinal];

Fai attenzione ai limiti dell'array.


3
+1 questa è di gran lunga la migliore soluzione IMHO perché si possono passare ordinali in particolare in android.os.Message.
likejudo,

9
Questo è molto preoccupante perché le matrici sono mutabili . Anche se valori [] è definitivo, non impedisce Suit.values[0] = Suit.Diamonds;da qualche parte nel codice. Idealmente ciò non accadrà mai, ma il principio generale di non esporre i campi mutabili è ancora valido. Per questo approccio, considera invece l'uso di Collections.unmodifiableListo simili.
Mshnik,

Che ne dici - valori statici finali privati ​​statici [] = valori (); public static Suit [] getValues ​​() {valori di ritorno; }
Pratyush,

4
@Pratyush che renderebbe immutabile la variabile array, ma non il suo contenuto. Potrei ancora fare getValues ​​() [0] = somethingElse;
Calabacin,

D'accordo, quindi il punto non è esporre Suit values[]direttamente o indirettamente (come è stato menzionato getValues()), io lavoro con un metodo pubblico in cui il ordinalvalore deve essere inviato come un argomento e restituire la Suitrappresentazione Suit values[]. Il punto qui (piastrella della domanda fin dall'inizio) era creare un tipo di enum da un enum ordinal
Manuel Jordan,

13

Sono d'accordo con la maggior parte delle persone sul fatto che usare ordinale sia probabilmente una cattiva idea. Solitamente risolvo questo problema dando all'enum un costruttore privato che può prendere ad esempio un valore DB e quindi creare una fromDbValuefunzione statica simile a quella nella risposta di 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;
    }

}

Ora puoi cambiare l'ordine senza cambiare la ricerca e viceversa.


Questa è la risposta esatta. Sono sorpreso che abbia ottenuto così pochi punti rispetto ad altre risposte più dirette ma potenzialmente non valide (a causa di modifiche del codice in futuro).
Calabacin,

8

È possibile utilizzare una tabella di ricerca statica:

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

3
Vedi anche Enums .
trashgod,

11
Wow! Wow! Questo è pulito, ovviamente, ma ... lo sai, il programmatore C dentro di me urla di dolore vedendo che assegni una HashMap completa ed esegui ricerche al suo interno SOLO per gestire essenzialmente 4 costanti: picche, cuori, diamanti e fiori! Il programmatore CA allocare 1 byte per ciascuno: 'const char CLUBS = 0;' ecc ... Sì, una ricerca HashMap è O (1), ma il sovraccarico di memoria e CPU di una HashMap, in questo caso rende molti ordini di grandezza più lenti e affamati di risorse rispetto alla chiamata diretta .values ​​() direttamente! Non c'è da meravigliarsi se Java è un tale sostenitore della memoria se la gente scrive così ...
Leszek,

2
Non tutti i programmi richiedono l'esecuzione di un gioco triplo A. In molti casi è giustificabile scambiare memoria e CPU per sicurezza del tipo, leggibilità, manutenibilità, supporto multipiattaforma, garbage collection, ecc. Lingue di livello superiore esistono per un motivo.
gennaio

3
Ma se il tuo intervallo di chiavi è sempre 0...(n-1), allora un array è meno codice e anche più leggibile; l'incremento delle prestazioni è solo un vantaggio. private static final Suit[] VALUES = values();e public Suit fromOrdinal(int ordinal) { return VALUES[ordinal]; }. Vantaggio extra: arresti anomali immediatamente su ordinali non validi, anziché restituire in silenzio null. (Non sempre un vantaggio. Ma spesso.)
Thomas

4

Questo è quello che uso. Non pretendo che sia molto meno "efficiente" delle soluzioni più semplici di cui sopra. Ciò che fa è fornire un messaggio di eccezione molto più chiaro di "ArrayIndexOutOfBounds" quando nella soluzione precedente viene utilizzato un valore ordinale non valido.

Utilizza il fatto che EnumSet javadoc specifica che l'iteratore restituisce gli elementi nel loro ordine naturale. C'è un'asserzione se ciò non è corretto.

Il test JUnit4 dimostra come viene utilizzato.

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

1

Questo è quello che faccio su Android con 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];
    }
}

è breve e non devo preoccuparmi di avere un'eccezione in Proguard


1

Puoi definire un metodo semplice come:

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

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

E usalo come:

System.out.println(Alphabet.get(2));

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

Apprezzo che condividi con noi se hai un codice più efficiente, il mio enum è enorme e costantemente chiamato migliaia di volte.


Penso che volevi dire "if (ordinal <1 || ordinal> 4) return null;"
geowar

0

Quindi un modo è quello di fare ciò ExampleEnum valueOfOrdinal = ExampleEnum.values()[ordinal];che funziona ed è facile, tuttavia, come accennato in precedenza, ExampleEnum.values()restituisce un nuovo array clonato per ogni chiamata. Questo può essere inutilmente costoso. Possiamo risolverlo memorizzando nella cache l'array in questo modo ExampleEnum[] values = values(). È anche "pericoloso" consentire la modifica del nostro array memorizzato nella cache. Qualcuno potrebbe scrivere, ExampleEnum.values[0] = ExampleEnum.type2;quindi lo renderei privato con un metodo di accesso che non esegue copie extra.

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

Dovresti usare ExampleEnum.value(ordinal)per ottenere il valore enum associatoordinal


-1

Ogni enum ha name (), che fornisce una stringa con il nome del membro enum.

Dato enum Suit{Heart, Spade, Club, Diamond}, Suit.Heart.name()darà Heart.

Ogni enum ha un valueOf()metodo, che accetta un tipo di enum e una stringa, per eseguire l'operazione inversa:

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

Perché qualcuno userebbe gli ordinali è al di là di me. Potrebbe essere più veloce di nanosecondi, ma non è sicuro, se i membri dell'enum cambiano, poiché un altro sviluppatore potrebbe non essere a conoscenza del fatto che un codice si basa su valori ordinali (specialmente nella pagina JSP citata nella domanda, l'overhead della rete e del database domina completamente il tempo, non utilizzando un numero intero su una stringa).


2
Perché il confronto di numeri interi è molto più veloce rispetto al confronto di stringhe?
HighCommander4,

2
Ma gli ordinali cambieranno se qualcuno modifica l'enum (aggiunge / riordina i memver). A volte si tratta di sicurezza e non di velocità, specialmente su una pagina JSP in cui la latenza della rete è di 1.000.000 volte la differenza tra il confronto di un array di numeri interi (una stringa) e un singolo numero intero.
Tony BenBrahim,

toString può essere ignorato, quindi potrebbe non restituire il nome enum. Il nome del metodo () è ciò che dà il nome enum (è definitivo)
gerardw,

anche i commenti sul codice e le versioni delle applicazioni sono una cosa (forse un formato di file è più semplice e più piccolo in questo modo)
joe pelletier

Non sono sicuro del motivo per cui questo è stato sottoposto a downgrade quando essenzialmente raccomanda la stessa cosa della risposta di oxbow_lakes. Sicuramente più sicuro dell'uso dell'ordinale.
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.