2012-01-04 25 views
6

Tengo curiosidad acerca de cómo funciona realmente Java cuando se trata de las declaraciones if. (Nota: cuando digo "componente" a continuación me refiero a las partes idividual controladas por el estado, por ejemplo a, b, c)Cómo maneja Java las declaraciones IF y la eficiencia

que es más eficiente en términos de cálculos?

if (a && b && c) { do stuff } 

o

if (a) { 
    if (b) { 
    if (c) { 
    do stuff } 
    } 
} 

La razón por la que pido es porque es importante lo que hace de Java en la primera versión. ¿Comprueba cada cosa en la declaración o marca a y si es false y luego cancela el control del resto de la declaración?

Si este es el caso, entonces tiene sentido colocar el componente como el primer componente en la declaración.

Si se comprueba toda la declaración cada vez, tiene más sentido dividir los componentes en varias instrucciones diferentes, como en el segundo ejemplo.

+3

Puede probarlo usted mismo fácilmente. – Howard

+0

Supongo que al hacer una, byc funciones que imprimen cuando se llaman, ¿verdad? – Chro

+2

@Howard: soy un gran defensor de probar cosas. Sin embargo, no creo que ejecutar algún código (o incluso mirar bytecodes) sea una buena forma de establecer si el comportamiento de cortocircuito está * garantizado *. Para esto, la especificación del lenguaje Java es el recurso definitivo. – NPE

Respuesta

15

En Java, && y || se garantiza un cortocircuito: los operandos se evalúan de izquierda a derecha, y la evaluación se detiene tan pronto como se sepa con certeza el resultado.

Desde el JLS:

El operador && es como & (§15.22.2), pero evalúa su operador de la derecha sólo si el valor de su operando de la izquierda es true. Es sintácticamente asociativo a la izquierda (se agrupa de izquierda a derecha).

El operador || es como | (§15.22.2), , pero evalúa su operando de la mano derecha solo si el valor de su operando de la izquierda es falso. Es sintácticamente asociativo a la izquierda (se agrupa de izquierda a derecha).

Esto significa que los dos fragmentos de código en su pregunta son exactamente equivalentes.

5

para (a && b && c), los "cortocircuitos" condicionales, lo que significa que tan pronto como uno falla, el resto no se evalúan.

Sin embargo, a menos que obtener a, b y c sea de alguna manera costoso, este podría ser un caso de optimización prematura.

Si algunos o todos son caros, a continuación, si lo hace

if (getA() && getB() && getC())...

entonces si getA() falla, los otros métodos son ni siquiera llama.

6

Estos fragmentos de código son completamente equivalentes y producir exactamente el mismo código de bytes:

0: iload_1 
1: ifeq 20 //a == false (0) 
4: iload_2 
5: ifeq 20 //b == false (0) 
8: iload_3 
9: ifeq 20 //c == false (0) 
12: //do stuff 
20: return 

Aunque la explicación es diferente, el generador de código produce la misma salida. En el primer caso debido a la evaluación perezosa, los términos subsiguientes no se evalúan si el anterior es false. En el segundo caso, el tiempo de ejecución no será más profundo si no se cumple la condición circundante if.

+0

¿Entonces básicamente estás diciendo que literalmente no importa? (excepto en términos de legibilidad del código) – Chro

+1

@ Chro: sí, en términos de rendimiento ** no importa **. Sin embargo, si el cálculo de, p. 'a' consume mucho tiempo, considere empujarlo más (o más), por lo que es más probable que nunca se evalúe. –

3

La mejor manera de comprobar este tipo de cosas es buscar en el Java Language Specification.

En este caso, desea section 15.23:

En tiempo de ejecución, la expresión operando de la izquierda se evalúa primero; si el resultado tiene un tipo Boolean, está sujeto a la conversión de unboxing (§5.1.8); si el valor resultante es falso, el valor de la expresión condicional y es falso y la expresión del operando de la derecha no se evalúa.

Así que no, en la expresión a && b && c, bc y se evaluó si no a dé como false.

Tenga en cuenta que esto no es parte del comportamiento de la declaración if - es parte del comportamiento del operador &&. Se puede ver el mismo efecto simplemente usando la expresión en otra parte:

// Still won't evaluate b and c if a is false... 
boolean x = a && b && c; 

(Vale la pena ser consciente de qué comportamiento es debido a que la parte de la lengua para cuando se desea consultar la especificación.)

2

En el primer caso, el if evalúa a false en la primera condición false encontrada (cortocircuitos). De lo contrario no sería capaz de hacer algo como:

if(a != null && a.toString().equals("b")) 

sin conseguir un NullPointerException si también se evaluaría la segunda parte.

+0

Muy útil, no había pensado en ese tipo de caso específico. Buen ejemplo. – Chro

1
if (a && b && c) { do stuff } 

Tiene cortocircuitos. && se cortocircuitará si a se evalúa como falso, lo que significa que el resto de la comprobación no está hecho.

Aparte de eso, creo que no hay diferencia entre los dos enfoques.

+0

Quiere decir si a evalúa a falso. – Tudor

+0

falso, seguramente? verdadero implica b también debe ser evaluado - esto es AND, no O – mcfinnigan

+0

@Tudor: Sí exactamente. true debería ir con '||'. Gracias. –

1

Este:

if (condition is met) { 
     //continute to next check 
      if (condition is met) { 
       //continue to next check 
        if (condition is met) { 
    do stuff } 
    } 
} 

hace lo mismo que: if (a && b && c) { do stuff }, pero ocupa mucho espacio. En realidad, no tiene que preocuparse por la eficiencia con este segmento de código en particular, por lo que ir con su primera opción es su mejor opción. También puede hacer

if (a && b){ 
if (condition is met) { 
     do stuff } 
} 
5

tenía curiosidad cómo el compilador tratar ambos casos por lo que he recopilado el siguiente código usando Java 1.6:

public class IfTest 
{ 
    public static void main(String[] args) 
    { 
     IfTest iffy = new IfTest(); 
     iffy.doit(); 
    } 

    private void doit() 
    { 
     Random rand = new Random(); 
     boolean a = rand.nextBoolean(); 
     boolean b = rand.nextBoolean(); 
     boolean c = rand.nextBoolean(); 

     if (a && b && c) 
     { 
      System.out.println("block 1"); 
     } 

     if (a) 
     { 
      if (b) 
      { 
       if (c) 
       { 
        System.out.println("block 2"); 
       } 
      } 
     } 
    } 
} 

... luego lo decompiló usando Jad. Esto es lo que el decompilador produjo a partir del archivo de clase:

// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov. 
// Jad home page: http://www.kpdus.com/jad.html 
// Decompiler options: packimports(3) 
// Source File Name: IfTest.java 

import java.util.Random; 

public class IfTest 
{ 

    public IfTest() 
    { 
    } 

    public static void main(String args[]) 
    { 
     IfTest iffy = new IfTest(); 
     iffy.doit(); 
    } 

    private void doit() 
    { 
     Random rand = new Random(); 
     boolean a = rand.nextBoolean(); 
     boolean b = rand.nextBoolean(); 
     boolean c = rand.nextBoolean(); 
     if(a && b && c) 
      System.out.println("block 1"); 
     if(a && b && c) 
      System.out.println("block 2"); 
    } 
} 

Supongo que no importa en qué dirección lo escriba.

Cuestiones relacionadas