¿Qué construcción "si" es más rápida: declaración u operador ternario?

83

Hay dos tipos de ifdeclaraciones en java - clásico: if {} else {}y taquigrafía: exp ? value1 : value2. ¿Es uno más rápido que el otro o son iguales?

declaración:

int x;
if (expression) {
  x = 1;
} else {
  x = 2;
}

operador ternario:

int x = (expression) ? 1 : 2;
Rogach
fuente
34
Supongo que no hay absolutamente ninguna diferencia. Es solo sintaxis. A menos que los compiladores sean algo malvados (o algo más) y me equivoque
sinelaw
4
¿Lo comparó (micro)? Comparta los resultados.
BalusC
3
Ambos se pondrán nerviosos. No habrá diferencia alguna. Y no se moleste en descompilar las cosas. Lo primero que hace HotSpot es eliminar todas las optimizaciones que aplica javac.
Ivo Wetzel
11
No existen para diferentes velocidades. Existen para diferentes propósitos. Estoy seguro de que comprende la diferencia entre declaraciones y expresiones. Las declaraciones realizan acciones. Las expresiones producen valores. ifes para uso en declaraciones. ?es para usar en expresiones.
Mike Dunlavey
3
+1 ya que vale la pena leer las respuestas a esta pregunta, incluso si la intención de la pregunta original está mal orientada.
jball

Respuestas:

106

Solo hay un tipo de declaración "si" allí. La otra es una expresión condicional. En cuanto a cuál funcionará mejor: podrían compilarse en el mismo código de bytes, y esperaría que se comporten de manera idéntica, o tan cerca que definitivamente no querrá elegir uno sobre el otro en términos de rendimiento.

A veces, una ifdeclaración será más legible, a veces el operador condicional será más legible. En particular, recomendaría usar el operador condicional cuando los dos operandos sean simples y sin efectos secundarios, mientras que si el propósito principal de las dos ramas son sus efectos secundarios, probablemente usaría una ifdeclaración.

Aquí hay un programa de muestra y un código de bytes:

public class Test {
    public static void main(String[] args) {
        int x;
        if (args.length > 0) {
            x = 1;
        } else {
            x = 2;
        }
    }

    public static void main2(String[] args) {
        int x = (args.length > 0) ? 1 : 2;
    }
}

Bytecode descompilado con javap -c Test:

public class Test extends java.lang.Object {
  public Test();
    Code:
       0: aload_0
       1: invokespecial #1
       4: return

  public static void main(java.lang.String[]
    Code:
       0: aload_0
       1: arraylength
       2: ifle          10
       5: iconst_1
       6: istore_1
       7: goto          12
      10: iconst_2
      11: istore_1
      12: return

  public static void main2(java.lang.String[
    Code:
       0: aload_0
       1: arraylength
       2: ifle          9
       5: iconst_1
       6: goto          10
       9: iconst_2
      10: istore_1
      11: return
}

Como puede ver, hay una ligera diferencia en istore_1el código de bytes aquí, ya sea que ocurra dentro del brance o no (a diferencia de mi intento anterior con grandes fallas :) pero me sorprendería mucho si el JITter terminara con un código nativo diferente.

Jon Skeet
fuente
s / declaración condicional / expresión condicional /
Laurence Gonsalves
1
Supongo que no quería decir para ambos mainy main2ser exactamente lo mismo?
ColinD
impresionante. No sabía que se podía compilar código de bytes hasta ahora.
Kyle
2
@Kyle: compilé Java, luego lo descompillé con javap.
Jon Skeet
1
@Kyle: Exactamente. En su mayoría, esperaba que el código de bytes fuera idéntico . Tal como está, es casi idéntico :)
Jon Skeet
10

Es probable que ambos ejemplos se compilen en un código de bytes idéntico o casi idéntico, por lo que no debería haber diferencia en el rendimiento.

Si hubiera habido una diferencia en la velocidad de ejecución, aún debería usar la versión más idiomática (que sería la segunda para asignar una sola variable basada en una condición simple y dos sub-expresiones simples, y la primera para realizar operaciones más complejas u operaciones que no caben en una sola línea).

Victor Nicollet
fuente
8

Estos son los mismos. Ambos son bastante rápidos, por lo general alrededor de 10-30 nano-segundos. (según el patrón de uso) ¿Es este período de tiempo importante para usted?

Debes hacer lo que creas que es más claro.

Peter Lawrey
fuente
4

Solo para agregar a todas las demás respuestas:

La segunda expresión a menudo se denomina operador / declaración terciario / ternario. Puede resultar muy útil porque devuelve una expresión. A veces, hace que el código sea más claro para declaraciones cortas típicas.

Secko
fuente
4
Gran ejemplo de esto en la práctica: en Java, si tengo que hacer un String final basado en el resultado de una expresión, puedo usar la sintaxis ternaria final String whichTable = (Integer.parseInt (clientId)> 500)? "serverClients": "offlineClients"; Entonces puedo usar el valor de la expresión en lugares donde whichTable debe ser final. Lo siguiente sería ilegal: final String whichTable = ""; if (Integer.parseInt (clientId)> 500) {whichTable = "serverClients"; } else {whichTable = "offlineClients"; }
James Perih
@JamesPerih En el caso de un finalcampo, podría usar bloques constructores para establecer un valor (aunque el operador condicional parece mil millones de veces mejor en mi opinión), y con variables locales, podría asignar un valor antes del primer uso más adelante en el bloque de código que Creo que el único caso en el que un ternario daría una ventaja sobre él if-elsees al llamar super(...)o this(...)dentro de un constructor.
Kröw
3

tampoco, se compilarán de la misma manera.

Freddie
fuente
0

El operador ternario es más rápido que la condición if-else.

public class TerinaryTest {
    public static void main(String[] args)
    {
        int j = 2,i = 0;
        Date d1 = new Date();
        for(long l=1;l<100000000;l++)
            if(i==1) j=1;
                else j=0;
        Date d2 = new Date();
        for(long l=1;l<100000000;l++)
            j=i==1?1:0;
        Date d3 = new Date();
        System.out.println("Time for if-else: " + (d2.getTime()-d1.getTime()));
        System.out.println("Time for ternary: " + (d3.getTime()-d2.getTime()));
    }
}

Resultados de la prueba:

Sendero-1:

Tiempo para if-else: 63

Tiempo para ternario: 31

Sendero-2:

Tiempo para if-else: 78

Tiempo para ternario: 47

Sendero-3:

Tiempo para if-else: 94

Tiempo para ternario: 31

Sendero-4:

Tiempo para if-else: 78

Tiempo para ternario: 47

rmkyjv
fuente
Obtuve exactamente los resultados opuestos al ejecutar su ejemplo, lo que muestra que los resultados no son confiables. Desafortunadamente, está cayendo en la trampa del microbenchmarking: es muy difícil hacer microbenchmarks correctamente. Para ver un par de ejemplos, puede ver aquí: stackoverflow.com/questions/2842695/what-is-microbenchmarking
Rogach
Su ejemplo particular sufre al menos de estos problemas: 4 ensayos no son suficientes, ejecuta las pruebas siempre en el mismo orden (primero if-else, segundo ternario), no calienta la JVM antes de ejecutar las pruebas, etc.
Rogach