2009-03-04 29 views
242

Ho il tipo di enum ReportTypeEnum che viene passato tra i metodi in tutte le mie classi ma devo quindi passare questo sull'URL in modo da utilizzare il metodo ordinale per ottenere il valore int. Dopo averlo ottenuto nella mia altra pagina JSP, ho bisogno di convertirlo in un ReportTypeEnum così posso continuare a passarlo.Convertire da enum ordinal a enum tipo

Come convertire l'ordinale in ReportTypeEnum?

Utilizzo di Java 6 SE.

+1

Non c'è Java 6 EE, fino ad ora (AFAIK). C'è Java SE 6 e Java EE 5. –

+0

Intendevo Java SE 6. – Lennie

risposta

504

Per convertire un numero ordinale nella sua represantation enum si potrebbe desiderare di fare questo:

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

Si prega di notare i limiti dell'array.

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

Code example on how to cache values().


La risposta è stata modificata per includere il feedback dato all'interno dei commenti

+140

Nota che ogni chiamata a values ​​() restituisce un array appena clonato, quindi potresti voler mettere in cache l'array se verrà chiamato in un punto critico regione del codice. – mattbh

+5

ad es. http://stackoverflow.com/a/19277247 – QED

+0

Ho implementato questa soluzione e non funziona per me. Restituisce il valore ordinale non è garantito per abbinare l'ordine in cui sono aggiunti i tipi enumerati. Non so che sia questa la risposta che sta sostenendo, ma volevo comunque avvisare le persone – IcedDante

128

Questo è quasi certamente una cattiva idea. Certamente se l'ordinale è di fatto persistente (ad esempio perché qualcuno ha aggiunto un segnalibro all'URL), significa che è necessario conservare sempre l'ordinamento enum in futuro, il che potrebbe non essere ovvio per codificare i manutentori lungo la linea.

Perché non codificare lo enum utilizzando myEnumValue.name() (e decodificare tramite ReportTypeEnum.valueOf(s))?

+3

idea molto migliore –

+0

Sono d'accordo, è la soluzione migliore. –

+16

Cosa succede se si modifica il nome dell'enumerazione (ma si mantiene l'ordine)? –

6

È potrebbe 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

Vedere anche [Enum] (http://download.oracle.com/javase/1.5.0/docs/guide/language/enums.html). – trashgod

+9

Wow! Solo wow! Questo è pulito, ovviamente, ma ... lo sai - il programmatore C dentro di me urla di dolore visto che assegni una HashMap in piena regola ed esegui le ricerche al suo interno tutto in SOLO per gestire essenzialmente 4 costanti: picche, cuori, diamanti e fiori! Un programmatore C assegnerebbe 1 byte per ciascuna: "const char CLUBS = 0;" ecc ... Sì, una ricerca HashMap è O (1), ma la memoria e il sovraccarico della CPU di una HashMap, in questo caso, rendono molti ordini di grandezza più lenti e affamati di risorse rispetto a chiamare direttamente .values ​​()! Non c'è da stupirsi che Java sia un tale maiale della memoria se le persone scrivono in questo modo ... – Leszek

+2

Non tutti i programmi richiedono l'esecuzione di una partita A tripla. In molti casi la memoria di trading e la CPU per tipo sicurezza, leggibilità, manutenibilità, supporto multipiattaforma, garbage collection, ecc ... sono giustificabili. I linguaggi di livello superiore esistono per una ragione. – Jan

-2

Ogni enum ha nome(), che dà una stringa con il nome di membro enum.

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

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

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

Perché chiunque userebbe gli ordinali è oltre me. Potrebbe essere più veloce di nanosecondi, ma non è sicuro, se i membri di enum cambiano, poiché un altro sviluppatore potrebbe non essere consapevole del fatto che alcuni codici si basano su valori ordinali (specialmente nella pagina JSP citata nella domanda, l'overhead di rete e database domina completamente il tempo, non usando un intero su una stringa).

+2

Perché confrontare gli interi è molto più veloce rispetto al confronto delle stringhe? – HighCommander4

+2

Ma i numeri ordinali cambieranno se qualcuno modifica l'enumerazione (Aggiunge/riordina i memori). 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 intero. –

+0

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

74

Se ho intenzione di utilizzare values() un sacco:

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

Nel frattempo ovunque.java:

Suit suit = Suit.values[ordinal]; 

Mente i limiti dell'array.

+2

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

+0

Questo è molto preoccupante perché ** gli array sono mutabili **. Anche se values ​​[] è 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 * continua a valere. Per questo approccio, considera invece l'uso di 'Collections.unmodifiableList' o simili. – Mshnik

5

Sono d'accordo con la maggior parte delle persone che usare l'ordinale è probabilmente una cattiva idea. Solitamente risolvo questo problema dando all'enumerazione un costruttore privato che può prendere ad esempio un valore di DB, quindi creare una funzione statica fromDbValue simile a quella nella risposta di Jan.

public 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 è possibile modificare l'ordine senza modificare la ricerca e viceversa.

4

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

Utilizza il fatto che EnumSet javadoc specifica l'iteratore restituisce gli elementi nel loro ordine naturale. C'è un assert se 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); 
} 
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]; 
    } 
} 

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

Mi rendo conto che voi condividete con noi se si dispone di un codice più efficiente, mio ​​enum è migliaia enormi e costantemente chiamati di volte.

0

Si supponga di avere il seguente enum:

public enum ErrorCodes { 
    BUSINESS_ERROR(100), SERVER_ERROR(500), NETWORK_ERROR(1000); 
} 

Per impostazione predefinita, non si sarebbe in grado di recuperare un CodiceErrore esempio dal suo valore numerico in quanto Java non fornisce alcun modo per recuperare Istanza Enum di un campo.

Per fare questo, normalmente esporre un hashmap statico all'interno della classe Enum che associa ciascun valore al suo esempio Enum corrispondente come segue:

public enum ErrorCodes { 
    BUSINESS_ERROR(100), SERVER_ERROR(500), NETWORK_ERROR(1000); 

    private int errorCode; 
    private static Map<Integer, ErrorCodes> errorCodeByErrorNumber = new HashMap<Integer, ErrorCodes>(); 

    static { 
     for (ErrorCodes errorCode : ErrorCodes.values()) { 
      errorCodeByErrorNumber.put(errorCode.getErrorCode(), errorCode); 
     } 
    } 

    private ErrorCodes(int errorCode) { 
     this.errorCode = errorCode; 
    } 

    public int getErrorCode() { 
     return errorCode; 
    } 

    public static ErrorCodes getErrorCodeByNumber(Integer errorNumber) { 
     return errorCodeByErrorNumber.get(errorNumber); 
    } 
} 

Per operazioni più comuni Enum, controllare questo: How to use Enums in java