2014-12-30 14 views
24

Diciamo che ho un enum con 100 valori. Per semplicità, prendere il seguente esempio:Conversione da stringa a enum Java con una grande quantità di valori

public enum code 
{ 
    CODE_1("string1"), 
    CODE_2("string2"), 
    CODE_3("string3"), 
    CODE_4("string4"), 
    ... 
} 

Voglio creare un metodo pubblico per convertire stringhe con un formato noto (come "stringa1", "stringa2" ...) al appropriata CODE_1 valore enum, CODE_2 ... In genere ciò avviene ripetendo tutti i valori e, se viene trovata una corrispondenza, restituisce tale valore enum. (I dettagli possono essere trovati in this question.)

Tuttavia, sono interessato al ciclo regolare su tutti i valori. Questo potrebbe potenzialmente essere un enorme collo di bottiglia? E se invece di 100 elementi, ce ne fossero 1000?

Come esercizio per me stesso, ho cercato di ottimizzare questa ricerca con una mappa statica, che può assicurare O (1) tempo di ricerca dato qualsiasi stringa. Mi piace questo trucco extra, ma voglio solo includerlo nel mio codice se è effettivamente necessario. Quali sono i tuoi pensieri e le tue conclusioni sull'uso del metodo iterativo rispetto al metodo della mappa?

public enum Code 
{ 
    ... 
    //enum values 
    ... 


    //The string-to-Code map 
    private static final Map<String,Code> CODE_MAP = populateMap(); 

    private static Map<String,Code> populateMap() 
    { 
     Map<String,Code> map = new HashMap<String,Code>(); 

     for(Code c : Code.values()) 
     { 
      map.put(c.getCode(), c); 
     } 

     return map; 
    } 


    private String code; 

    private Code(String code) 
    { 
     this.code = code; 
    } 

    public String getCode() 
    { 
     return this.code; 
    } 

    public Code convertFromString(String code) 
    { 
     //assume that the given string is actually a key value in the map 

     return (Code) CODE_MAP.get(code); 
    } 
} 
+3

vorrei andare con la Soluzione 'Map'. –

+0

È bene usare la mappa invece di iterarla manualmente. –

+0

Hai profilato e trovato l'ingenuo, iterativo modo era un collo di bottiglia? Se non lo hai fatto, non devi preoccuparti di questo: focalizza la tua attenzione sul codice che in realtà è un collo di bottiglia. – corsiKa

risposta

29

Si desidera un Map<String, Code>, ma come popolarlo ordinatamente? Enums non consentono di inizializzare un campi statici prima che le istanze enum vengono inizializzati, ma c'è un piccolo trucco accurato, chiamato Initialization-on-demand holder idiom, che rende utilizzando una mappa inizializzata staticamente necessaria per questa funzionalità facile da implementare:

public enum Code { 
    CODE_1("string1"), 
    CODE_2("string2"), 
    CODE_3("string3"), 
    // etc 
    ; 

    private static class Holder { 
     static Map<String, Code> CODE_MAP = new HashMap<>(); 
    } 

    private final String code; 

    private Code(String code) { 
     this.code = code; 
     Holder.CODE_MAP.put(code, this); 
    } 

    public String getCode() { 
     return this.code; 
    } 

    public Code convertFromString(String code) { 
     return CODE_MAP.get(code); 
    } 
} 

Funziona perché il programma di caricamento classi inizializza le classi statiche interne prima di inizializzando la classe enum, quindi la mappa viene assegnata pronta per essere caricata durante l'inizializzazione dell'istanza dell'enum.

Nessun anello. Nessun codice speciale per caricare la mappa (fatto nel costruttore). Codice minimo

+2

Quindi stai dicendo che il modo in cui lo popolo nel mio codice sopra non è "pulito"? Potresti spiegare perché non è "pulito"? Dici anche: "Le enumerazioni non ti permettono di inizializzare un campo statico prima che le istanze di enum siano inizializzate", ma non credo che lo stia facendo. Sto popolando la mappa dopo le mie istanze enum? – user1884155

+2

@ user1884155 il tuo codice non è accurato perché c'è così tanto se questo - metodi extra, più linee. Questo cude utilizza solo 1 riga per l'inizializzazione della mappa (o 2 righe se si conta la riga 'class Holder') e solo 1 riga per caricarla, senza definire metodi aggiuntivi - che sembrerebbe ragionevole chiamare" più ordinata ". Per quanto riguarda le enumerazioni che non consentono l'inizializzazione statica, voglio dire che non puoi farlo: 'mappa statica privata CODE_MAP = new HashMap <>();' - se lo fai e prova a usarlo, sarà nullo durante l'inizializzazione dell'istanza dell'enum, quindi non è possibile caricarlo correttamente come ho fatto dal costruttore di enum. – Bohemian

+0

@Bohemian La classe statica interna è strettamente necessaria? Una soluzione simile in [Come fare la ricerca inversa nelle enumerazioni] (http://howtodoinjava.com/2012/12/07/guide-for-understanding-enum-in-java/#reverse_lookup) rende la mappa 'statica finale privata 'direttamente nell'enum. –

4

Map è buona opzione: codice più pulito e O(1). Se si utilizza for-loop, il meglio che si ottiene è O(n)

4

La soluzione fornita è una corretta implementazione.

Come si dovrà esporre solo un metodo ed è più leggibile.

Ed è sempre bene utilizzare Map anziché iterating it manually.

E anche come hai menzionato la complessità è O(1).

+1 to your question, in quanto fornisce a cleaner approach to use enum in some usecases.

3

Bene, le alternative alla soluzione di mappa sarebbero una gigantesca dichiarazione di commutazione (che potrebbe essere generata automaticamente) o una ricerca binaria in una matrice contenente le stringhe. Credo che nessuno dei due batterà le prestazioni di HashMap con un ampio margine, anche se se è davvero importante, probabilmente starai meglio con il benchmarking.

Una cosa che non è stata menzionata è come Enum.valueOf() consente di trasformare una stringa in un valore enum, se ha il nome esatto di uno dei membri enum. Se questa è una possibilità nel tuo caso (osservando il tuo esempio, non vedo come Code.CODE_1 non possa essere facilmente rinominato Code.string1 ecc.), Suggerirei di usarlo, poiché non richiede alcuna codifica aggiuntiva a tutti e sarà quindi il più comprensibile.

4

Se il valore del codice stringa è un noto e formato coerente si potrebbe evitare l'uso di una cartina e la memoria che consuma e costruire il valore di ricerca CODE enum al volo:

public static Code convertFromString(String code) { 
    return valueOf("CODE_" + code.substring("string".length())); 
    } 
+0

Sì, il formato non è così coerente che ho appena usato questo semplice esempio parseable per porre la domanda. – user1884155

Problemi correlati