tutorialspoint statement jtls example elseif else java if-statement micro-optimization

statement - Java: if-return-if-return vs if-return-elseif-return



jstl if statement (5)

Hizo una pregunta no relacionada donde tenía un código como este:

public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; // Check property values }

Recibí un comentario que decía que esto no era óptimo, y que en su lugar (si lo entendía correctamente) debería hacer esto:

public boolean equals(Object obj) { if (this == obj) return true; else if (obj == null) return false; else if (getClass() != obj.getClass()) return false; // Check property values }

Debido a las declaraciones de devolución, realmente no puedo ver por qué alguno de ellos debería ser más eficiente o más rápido que el otro. Dado un determinado objeto, ambos métodos tendrían que hacer un número igual de comprobaciones por lo que puedo ver. Y debido a las declaraciones de devolución, no se ejecutará ningún código adicional en ninguna de ellas.

¿Me estoy perdiendo de algo? ¿Hay algo para eso? ¿Hay algunas optimizaciones del compilador o algo así o lo que sea?

Sé que esto es micro optimización y lo más probable es que me quede con el primero de todos modos, ya que creo que se ve más limpio con todos los ifs en la misma posición. Pero no puedo evitarlo; ¡Soy curioso!


Creo que este código se puede mejorar un poco (ojo, es muy legible):

if (obj == null) return false; if (getClass() != obj.getClass()) return false;

El operador instanceof es equivalente a ambos combinados y probablemente sea más rápido, menos código y no hay invocaciones de método:

if (!(obj instanceof MyClass)) return false;

Pero qué sé ... Soy demasiado perezoso para analizar el código de bytes (nunca antes lo había hecho). :-pag


El código de bytes generado es idéntico para esos dos casos, por lo que es puramente una cuestión de estilo.

e1 dos métodos e1 y e2 y ambos produjeron este código de bytes (lea usando javap -v ):

public boolean e1(java.lang.Object); Code: Stack=2, Locals=2, Args_size=2 0: aload_0 1: aload_1 2: if_acmpne 7 5: iconst_1 6: ireturn 7: aload_1 8: ifnonnull 13 11: iconst_0 12: ireturn 13: aload_0 14: invokevirtual #25; //Method java/lang/Object.getClass:()Ljava/lang/Class; 17: aload_1 18: invokevirtual #25; //Method java/lang/Object.getClass:()Ljava/lang/Class; 21: if_acmpeq 26 24: iconst_0 25: ireturn

Olvidé el código que puse después para hacerlo compilar.


Ninguno de los dos es más eficiente que el otro. El compilador puede ver fácilmente que los dos son idénticos, y de hecho Suns / Oracles javac produce un bytecode idéntico para los dos métodos.

Aquí hay una clase IfTest :

class IfTest { public boolean eq1(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; return true; } public boolean eq2(Object obj) { if (this == obj) return true; else if (obj == null) return false; else if (getClass() != obj.getClass()) return false; return true; } }

Lo compilé con javac y el desmontaje es el siguiente:

public boolean eq1(java.lang.Object); Code: 0: aload_0 1: aload_1 2: if_acmpne 7 5: iconst_1 6: ireturn 7: aload_1 8: ifnonnull 13 11: iconst_0 12: ireturn 13: aload_0 14: invokevirtual #2; //Method Object.getClass:()Ljava/lang/Class; 17: aload_1 18: invokevirtual #2; //Method Object.getClass:()Ljava/lang/Class; 21: if_acmpeq 26 24: iconst_0 25: ireturn 26: iconst_1 27: ireturn

public boolean eq2(java.lang.Object); Code: 0: aload_0 1: aload_1 2: if_acmpne 7 5: iconst_1 6: ireturn 7: aload_1 8: ifnonnull 13 11: iconst_0 12: ireturn 13: aload_0 14: invokevirtual #2; //Method Object.getClass:()Ljava/lang/Class; 17: aload_1 18: invokevirtual #2; //Method Object.getClass:()Ljava/lang/Class; 21: if_acmpeq 26 24: iconst_0 25: ireturn 26: iconst_1 27: ireturn

Es decir, recomendaría usar la primera versión (sin el else ). Algunas personas pueden argumentar que es más limpio con las partes más, pero yo diría lo contrario. Incluir el else indica que el programador no se dio cuenta de que era innecesario.


No veo ninguna razón práctica para reemplazar una de esas implementaciones con la otra, en cualquier dirección.

El segundo ejemplo tendría sentido si quisiera evitar declaraciones de devolución múltiples en un método: algunas personas prefieren esa forma de codificación. Entonces necesitamos los constructores if-else if:

public boolean equals(Object obj) { boolean result = true; if (this == obj) result = true; else if (obj == null) result = false; else if (getClass() != obj.getClass()) result = false; return result; }


Piénsalo de esta manera. Cuando se ejecuta una declaración de devolución, el control abandona el método, por lo que el else realmente no agrega ningún valor, a menos que desee argumentar que agrega legibilidad (lo que realmente no creo que haga, pero otros pueden estar en desacuerdo).

Entonces cuando tienes:

if (someCondition) return 42; if (anotherCondition) return 43;

Realmente no tiene ningún valor agregar un else al segundo if .

De hecho, uso una herramienta al escribir el código de C # llamado Resharper , y en realidad marcará el else como código inútil en estas situaciones. Entonces, creo que, en general, es mejor dejarlos fuera. Y como ya mencionó Joachim, el compilador los optimiza de todos modos.