2013-04-01 8 views
5

Su innumerevoli (beh, numerabili, ma molte) occasioni, specialmente all'interno di un metodo/funzione di una classe, sono stato in una situazione in cui voglio fare una serie di operazioni all'interno una funzione di annullamento del reso, ma solo if(condition met). Nella maggior parte dei casi, posso vedere come (supponendo che il codice funzioni) un'istruzione else possa essere rimossa del tutto semplicemente ritornando nel blocco if.Un'altra istruzione rallenta la velocità di elaborazione/tempo di esecuzione? (In situazioni in cui uno potrebbe essere evitato)

Ecco un esempio specifico, nel caso in cui non aveva senso:

Con un'istruzione else (come un insegnante avrebbe mostrato)

private void ifThisDoThat(params){ 
     if(dependenciesNotMet) return; 
     else{ 
      ////DO STUFF HERE... 
     } 
    } 

Senza (più parsimoniosi)

private void ifThisDoThat(params){ 
     if(dependenciesNotMet) return; 
     //Assuming the above does not execute, DO STUFF HERE... 
    } 

Sto pensando di rimuovere la dichiarazione else , se l'ottimizzazione fosse del tutto, sarebbe stata classificata come micro-ottimizzazione, ma immaginavo comunque che avrei chiesto la mia edificazione.

In chiusura:

Esistono vantaggi di utilizzare un return per rimuovere un blocco else?

Il compilatore esegue un lavoro aggiuntivo se utilizzo un'istruzione else?

C'è un motivo per utilizzare sempre il else (in caso di errori o per altri motivi)?

+0

Non penso che return void sia l'esempio migliore, perché 'if (dependenciesMet) {// Do stuff}' generalmente sarebbe meglio del primo modo secondo me. – Dukeling

+0

Sto iniziando a capire che è più una decisione stilistica che altro. Capisco che se le istruzioni, una volta compilate, "branch" (* Alla fine dell'istruzione if * 'BRA [someLabel]'), ed evitando un ramo _can_ riducono il tempo di compilazione, ma le istruzioni 'else' sono compilate (per tutte le apparenze) in allo stesso modo di un normale blocco di codice, eccetto che sono specificatamente referenziati alla fine delle istruzioni compilate dell'istruzione if. In altre parole, non c'è differenza. Ah bene. EDIT: Tranne che un'altra istruzione genera un'etichetta (che sembra insignificante?). – Jordan

risposta

5

È una falsa ottimizzazione.Il compilatore potrebbe richiedere più tempo per la compilazione, con un ritorno dal centro, e qualsiasi compilatore di ottimizzazione produrrà essenzialmente lo stesso codice eseguibile per entrambe le forme.

In termini di stile, a volte uno stile è buono, a volte un altro. I pericoli dello stile di ritorno immediato sono:

  1. Se nella parte inferiore del metodo è presente una logica di rifinitura/ripulitura, questa verrà ignorata. È facile dimenticare, in un metodo di grandi dimensioni, che la logica è presente, ed è abbastanza facile, in un metodo di grandi dimensioni, modificare tale logica in un metodo che in precedenza non l'aveva. Bug di questo tipo possono essere difficili da trovare.
  2. Poiché essenzialmente elimina l'opzione di avere una logica di "pulizia" nella parte inferiore del metodo, può portare a una proliferazione della logica nelle singole gambe if, creando più confusione rispetto allo standard if/then/else.
  3. È contrario a una buona pratica di "programmazione strutturata".

Detto questo, ci sono casi in cui lo stile di ritorno-from-the-middle è una scelta migliore:

  1. I casi in cui ci sono più sequenziali if dichiarazioni, ognuna con un corpo semplice e ciascuno in grado di finire con il ritorno.
  2. Il caso di un metodo molto breve in cui la "uscita veloce" è naturale ed abbastanza evidente.
  3. Il caso di una "uscita rapida" (ad esempio, poiché alcuni dati sono nulli) molto vicino alla cima di un metodo più lungo.
+0

Sì, "uscita veloce" suona come quello che stavo cercando di dire, ma con parole migliori.Se c'è un codice che finirebbe per essere un lavoro extra che porta a un'eccezione (che richiede un try/catch o qualche assurdità), usare un rapido ritorno in cima sembra l'opzione preferibile (e in definitiva più leggibile). Grazie! – Jordan

+2

Aggiungo che ci sono molte cose che possono migliorare la leggibilità senza effettivamente cambiare la logica if/then/else. Preferisco fortemente vedere '{' sulla riga 'if' piuttosto che sulla riga successiva, e la corrispondenza'} 'è allineata con' if'. Se c'è un 'else', preferisco vedere' else {'sulla sua stessa riga, non'} else {'. Le linee vuote possono essere aggiunte o rimosse per migliorare la leggibilità - è un po 'un'arte. Evita le lunghe code (difficile con Objective-C, ad esempio, non tanto per Java). Usa nomi di variabili brevi in ​​piccoli metodi, quelli più lunghi in metodi più grandi con più variabili locali. Ecc. –

+0

+1 Per l'argomento generale. Ma sul punto di ripulire la logica e i metodi lunghi, direi che quella parte del corpo del metodo dovrebbe essere estratta nel proprio metodo, e quindi, se ancora desiderabile, può essere richiamata appena prima che venga richiamato qualsiasi ritorno di luogo. –

1

È un problema di stile. Non dovrebbe influenzare il codice generato. Non è un'ottimizzazione.

Personalmente, utilizzo sempre il reso se è possibile perché penso che renda il codice più leggibile - non è necessario cercare in giro per la fine dell'istruzione else.

+0

Sono d'accordo con la leggibilità. Questo è il motivo per cui ho già intenzione di evitare la dichiarazione else - un minor numero di parentesi graffe. Grazie! (Ma ancora curioso di sapere se il compilatore "fa qualcosa" con la dichiarazione else) – Jordan

2

Un test rapido può rispondervi. Immaginate di avere:

public void x(int i){ 
    if(i == 0){ 
     System.out.println("zero"); 
     return; 
    } 
    System.out.println("not zero"); 
} 

public void y(int i){ 
    if(i == 0){ 
     System.out.println("zero"); 
     return; 
    } 
    else { 
     System.out.println("not zero"); 
    } 
} 

Se si dà un'occhiata al codice compilato (uso javap -v <class>):

Codice in x:

public void x(int); 
    flags: ACC_PUBLIC 
    Code: 
     stack=2, locals=2, args_size=2 
     0: iload_1  
     1: ifne   13 
     4: getstatic  #16     // Field java/lang/System.out:Ljava/io/PrintStream; 
     7: ldc   #22     // String zero 
     9: invokevirtual #24     // Method java/io/PrintStream.println:(Ljava/lang/String;)V 
     12: return   
     13: getstatic  #16     // Field java/lang/System.out:Ljava/io/PrintStream; 
     16: ldc   #30     // String not zero 
     18: invokevirtual #24     // Method java/io/PrintStream.println:(Ljava/lang/String;)V 
     21: return   
     LineNumberTable: 
     line 6: 0 
     line 7: 4 
     line 8: 12 
     line 10: 13 
     line 11: 21 
     LocalVariableTable: 
     Start Length Slot Name Signature 
       0  22  0 this Lpt/kash/Test; 
       0  22  1  i I 
     StackMapTable: number_of_entries = 1 
      frame_type = 13 /* same */ 

Codice in y:

public void y(int); 
    flags: ACC_PUBLIC 
    Code: 
     stack=2, locals=2, args_size=2 
     0: iload_1  
     1: ifne   13 
     4: getstatic  #16     // Field java/lang/System.out:Ljava/io/PrintStream; 
     7: ldc   #22     // String zero 
     9: invokevirtual #24     // Method java/io/PrintStream.println:(Ljava/lang/String;)V 
     12: return   
     13: getstatic  #16     // Field java/lang/System.out:Ljava/io/PrintStream; 
     16: ldc   #30     // String not zero 
     18: invokevirtual #24     // Method java/io/PrintStream.println:(Ljava/lang/String;)V 
     21: return   
     LineNumberTable: 
     line 14: 0 
     line 15: 4 
     line 16: 12 
     line 19: 13 
     line 21: 21 
     LocalVariableTable: 
     Start Length Slot Name Signature 
       0  22  0 this Lpt/kash/Test; 
       0  22  1  i I 
     StackMapTable: number_of_entries = 1 
      frame_type = 13 /* same */ 

La differenza è ... nessuna. Il compilatore è abbastanza intelligente per ottimizzare il codice.

Così, la linea di fondo è (come è stato detto): Ottimizza per leggibilità e di semplicità

0

Non v'è alcuna differenza di esecuzione, ottimizzerà la stessa cosa in ogni modo, ma per migliorare la leggibilità Consiglio questo (usando un principio di ritorno):

private void ifThisDoThat(params){ 
    if(!dependenciesNotMet) { // would be nicer to change this to if(dependenciesNotMet) 
     ////DO STUFF HERE... 
    } 
} 
0

Testando il programma con javap, difficilmente vedo una differenza tranne un'istruzione JUMP 14: goto 25 nel secondo caso.

non vedo un motivo per aggiungere altro, se non hai niente da eseguire su else .È può decidere di usarlo comunque per fare la registrazione ecc

public class Test { 
    public static void main(String[] s){ 
     if(testMethod()){ 
      System.out.println("in if"); 
     } 
     System.out.println("in else");  
    } 
    static boolean testMethod(){ 
     return false; 
    } 
} 

è conseguente

public static void main(java.lang.String[]); 
     Code: 
     Stack=2, Locals=1, Args_size=1 
     0: invokestatic #16; //Method testMethod:()Z 
     3: ifeq 14 
     6: getstatic  #20; //Field java/lang/System.out:Ljava/io/PrintStream; 
     9: ldc  #26; //String in if 
     11: invokevirtual #28; //Method java/io/PrintStream.println: 
          (Ljava/lang/String;)V 
     14: getstatic  #20; //Field java/lang/System.out:Ljava/io/PrintStream; 
     17: ldc  #34; //String in else 
     19: invokevirtual #28; 
     //Method java/io/PrintStream.println:(Ljava/lang/String;)V 
    22: return 

e

public class Test { 
    public static void main(String[] s){ 
     if(testMethod()){ 
      System.out.println("in if"); 
     }else{ 
      System.out.println("in else"); 
     } 
    } 
    static boolean testMethod(){ 
     return false; 
    } 
} 

produrrebbe,

public static void main(java.lang.String[]); 
    Code: 
    Stack=2, Locals=1, Args_size=1 
    0: invokestatic #16; //Method testMethod:()Z 
    3: ifeq 17 
    6: getstatic  #20; //Field java/lang/System.out:Ljava/io/PrintStream; 
    9: ldc  #26; //String in if 
    11: invokevirtual #28; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 
    14: goto 25 
    17: getstatic  #20; //Field java/lang/System.out:Ljava/io/PrintStream; 
    20: ldc  #34; //String in else 
    22: invokevirtual #28; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 
    25: return 
+0

Sì, ho commentato sopra che (da una rapida ricerca su Google) l'unica differenza sembra essere che viene creata una nuova etichetta per la dichiarazione else, quindi fatta riferimento alla fine del if (nel caso in cui la condizione sia falsa). Hai verificato questo per me. Grazie! – Jordan

Problemi correlati