2011-02-07 18 views
9

Non ho avuto la vera fortuna di ottenere una risposta concisa per questo confronto utilizzando Google e piuttosto che fare le mie valutazioni di tempo, ho pensato di chiedere prima.Java: prestazioni di Enums vs. if-then-else

Sono abbastanza sicuro che un'istruzione switch che utilizza Enums eseguirà più rapidamente di un'istruzione if-then-else, anche se è o meno una differenza notevole un'altra domanda.

Qualcuno potrebbe far luce su questo per me?


Grazie per le risposte rapide ragazzi, lo terrò a mente per progetti futuri.

+2

Dubito fortemente che la differenza di prestazioni sarà qualcosa di più della micro ottimizzazione. Vorrei andare per switch stmt se fornisce codice più leggibile e manutenibile nel tuo caso. – CoolBeans

+0

Sono curioso, cosa ti fa dire "Sono abbastanza sicuro che un'istruzione switch che usa Enums potrebbe funzionare più velocemente di una dichiarazione if-then-else"? – Pops

+0

@Lord Torgamus - In un'istruzione switch, ogni caso è vero o falso, in un'istruzione if-then-else, ciascuno se può avere più valori booleani, cioè questo && quello, o questo || quello, ecc. E mi limito a capire, valutare più di uno richiederebbe un po 'più di tempo. – FizzBuzz

risposta

8

Yeap, sì, perché in termini generali un'istruzione switch funziona più rapidamente di if/else chain.

Sebbene il codice byte generato non sia sempre la fonte definitiva per i confronti delle prestazioni, è possibile esaminarlo per avere un'idea migliore.

Per esempio questo codice:

class A { 
    enum N { ONE, TWO, THREE } 
    void testSwitch(N e) { 
     switch(e) { 
      case ONE : x(); break; 
      case TWO : x(); break; 
      case THREE : x(); break; 
     } 
    } 
    void testIf(Enum e) { 
     if(e == N.ONE) { x(); } 
     else if(e == N.TWO) { x(); } 
     else if(e == N.THREE) { x(); } 
    } 
    void x(){} 
} 

genera i seguenti:

Compiled from "A.java" 
class A extends java.lang.Object{ 
A(); 
    Code: 
    0: aload_0 
    1: invokespecial #1; //Method java/lang/Object."<init>":()V 
    4: return 

void testSwitch(A$N); 
    Code: 
    0: getstatic #2; //Field A$1.$SwitchMap$A$N:[I 
    3: aload_1 
    4: invokevirtual #3; //Method A$N.ordinal:()I 
    7: iaload 
    8: tableswitch{ //1 to 3 
     1: 36; 
     2: 43; 
     3: 50; 
     default: 54 } 
    36: aload_0 
    37: invokevirtual #4; //Method x:()V 
    40: goto 54 
    43: aload_0 
    44: invokevirtual #4; //Method x:()V 
    47: goto 54 
    50: aload_0 
    51: invokevirtual #4; //Method x:()V 
    54: return 

void testIf(java.lang.Enum); 
    Code: 
    0: aload_1 
    1: getstatic #5; //Field A$N.ONE:LA$N; 
    4: if_acmpne 14 
    7: aload_0 
    8: invokevirtual #4; //Method x:()V 
    11: goto 39 
    14: aload_1 
    15: getstatic #6; //Field A$N.TWO:LA$N; 
    18: if_acmpne 28 
    21: aload_0 
    22: invokevirtual #4; //Method x:()V 
    25: goto 39 
    28: aload_1 
    29: getstatic #7; //Field A$N.THREE:LA$N; 
    32: if_acmpne 39 
    35: aload_0 
    36: invokevirtual #4; //Method x:()V 
    39: return 

void x(); 
    Code: 
    0: return 

} 

che sembra essere abbastanza veloce in entrambi i casi.

Quindi, scegli quello che è più facile da mantenere.

+0

sebbene gli switch siano in genere più veloci, un esempio di quando un interruttore non è così buono è quando le affermazioni del caso sono scarse e/o non in ordine, quindi può perdere i suoi vantaggi ... ma come sempre con qualsiasi micro ottimizzazione - i test di temporizzazione sono l'unica vera risposta :-) – SteelBytes

+0

Tre valori di enumerazione non stanno andando a fare una differenza. Prova con 100 e vedrai una tabella di ricerca rispetto a 100 confronti. – OrangeDog

+0

@Orange e invoca un metodo diverso in ciascuno, ovviamente. – OscarRyz

1

Non so più veloce, immagino che siano entrambi incredibilmente veloci.

La mia considerazione è che l'interruttore con enumerazioni è molto più leggibile di un multi-if/else bloccare

Ma attenzione di perdere istruzioni break !!

0

In teoria, un'istruzione switch può essere ottimizzata come un singolo salto calcolato, mentre le catene if-then-else devono rimanere come confronti individuali. Non so se Java in realtà esegue questa ottimizzazione.

In ogni caso, gli switch sono migliori delle catene if-then-else in termini di leggibilità e manutenibilità, quindi usali comunque se possibile.

+0

Dalla memoria esegue questo salto calcolato (può farlo facilmente perché limita i tipi che possono essere usati in uno switch su stringhe primitive, enumerate e (in Java 7 e oltre) – berry120

+0

@ berry120 - Stavo pensando al ottimizzazione ottimale (riempire i casi con i nops quindi andare avanti 'enum.ordinal() * sizeOfCase'). – OrangeDog

1

Sì, un'istruzione switch eseguirà quasi sempre più rapidamente del blocco equivalente di istruzioni if ​​/ else perché il compilatore può eseguire più ottimizzazioni (in genere un blocco di interruzioni viene compilato in una tabella di diramazione che è praticamente impossibile da eseguire con un blocco dei condizionali.)

direi che sono anche sia più leggibile e più gestibile (con l'eccezione dei casi usando caduta-through, che vorrei consigliare contro!)

quanto alla questione se è notevolmente più veloce, dipende da ciò che definite come notevole. Le probabilità sono a meno che non cerchi qualcosa di veramente specifico che non te ne accorgi affatto, ma farei comunque le cose in questo modo a causa del vantaggio di leggibilità più che altro (considera il vantaggio di velocità come bonus!)

+0

" (in genere un blocco di commutazione viene compilato in una tabella di diramazione che è praticamente impossibile fare con un blocco di condizionali.) "- ma se il compilatore è abbastanza intelligente da vedere che è solo un elenco di variabili rispetto alle costanti (numeriche) (che è ciò che le enumerazioni sono, in profondità), non sarebbe in grado di ottimizzarlo come se fosse un switch/case? – fwielstra

+0

@Cthulhu - Avrei dovuto dire che è impossibile fare con il caso generale di un blocco di condizionali Sì, nel caso in cui lo descrivi teoricamente sarebbe, anche se non sono sicuro che il compilatore la sua analisi (IMO ci sono ottimizzazioni migliori su cui vale la pena concentrarsi comunque). – berry120

6

Basta aderire al codice più leggibile e comprensibile che riesci a trovare, sono sicuro che hai perso tutto il tempo guadagnato nell'ottimizzazione delle prestazioni mentre cercavi già questa risposta.Le micro-ottimizzazioni come questa raramente valgono la pena e possono facilmente portare a un codice più complesso del necessario.

1

La mia risposta a questo è la stessa di sempre per la domanda è la costruzione del linguaggio X generalmente più veloce della costruzione del linguaggio Y: Non c'è una risposta generale!

Potrebbe esserci solo una risposta specifica per alcune implementazioni di una lingua, ad es. La JVM di Oralce (formalmente Sun) basata su compilatore Hotspot o IBM JDK su piattaforma Z o OpenJDK su Linux, o ...

Quindi, l'unico modo per dare una risposta significativa alla tua domanda è di fare un punto di riferimento adeguato. Fai attenzione ai micro benchmark, sono più spesso sbagliati che a destra, vedi per es. How not to write a micro benchmark. Se si vuole ancora trovare su utilizzare questa domanda framework descritto here.

Pertanto, vorrei consigliare di selezionare la caratteristica linguistica in base alla loro applicabilità e leggibilità nel proprio contesto.