martes, 22 de febrero de 2011

Diferencias entre método equals() y operador ==

Si comparamos tipos primitivos con los operadores == o != el resultado es el esperado. Sin embargo, si comparamos objetos con estos dos operadores el resultado puede no ser el esperado. Tenemos el ejemplo siguiente:
 public class Equivalencia {
   public static void main(String[] args) {
     Integer n1 = new Integer(47);
     Integer n2 = new Integer(47);
 
     System.out.println("n1==n2: "+n1 == n2);
     System.out.println("n1!=n2: "+n1 != n2);

     String s1 = new String("Hola");
     String s2 = new String("Hola");

     System.out.println("s1==s2: "+s1 == s2);
     System.out.println("s1!=s2: "+s1 != s2);
   }
} 

Si lo ejecutamos el resultado es:
n1==n2: false
n1!=n2: true
s1==s2: false
s1!=s2: true


Esto se debe a que los operadores == y != comparan referencias, no valores. La referencia de n1 está almacenada en un lugar de la memoria y la de n2 en otra, lo mismo para s1 y s2. Recuerda que cada vez que se crea un objeto con new se le asigna un espacio de memoria en el cúmulo.
Para comparar objetos, no tipos primitivos, tenemos el método equals(), que se puede utilizar siempre, ya que está incluido en la clase java.lang.Object que por defecto heredan todas las clases. Este método compara valores en lugar de referencias, modificamos un poco el ejemplo anterior y utilizamos equals():
 public class Equivalencia2 {
   public static void main(String[] args) {
     Integer n1 = new Integer(47);
     Integer n2 = new Integer(47);
 
     System.out.println("n1.equals(n2): "+n1.equals(n2));

     String s1 = new String("Hola");
     String s2 = new String("Hola");

     System.out.println("s1.equals(s2): "+s1.equals(s2));
   }
} 

El resultado es:
n1.equals(n2): true
s1.equals(s2): true

Sin embargo, fijémonos en los dos siguientes ejemplos:
class Value {
   int i;
}
 
public class Equivalencia3 {
   public static void main(String[] args) {
      Value v1 = new Value();
      Value v2 = new Value();
 
      v1.i = v2.i = 100;
      System.out.println("v1.i=v2.i=100");
      System.out.println("v1.equals(v2): "+v1.equals(v2));
   }
}

El resultado es:
v1.i=v2.i=100
v1.equals(v2): false

El ejemplo siguiente es una variación del Ejercicio 6 del Tema 3. Este ejercicio lo he hecho de varias maneras.
class Dog2{

   String name;
   String says;

   Dog2(String n,String s){
      name=n;
      says=s;
   }

   public String nombre(){
      return name;
   }

   public String says(){
      return says;
   }
}

public class Ejer_6b{

   public static void main(String args[]){
   
      Dog2 dogSpot=new Dog2("Spot","Ruff");
      Dog2 dogScruffy=new Dog2("Scruffy","Wurf");
      Dog2 dogNuevo=new Dog2("Spot","Ruff");
 
      System.out.println("Tengo dos perritos.");
      System.out.println("Uno se llama: "+dogSpot.nombre()+" y ladra diciendo: "+dogSpot.says());
      System.out.println("El otro se llama: "+dogScruffy.nombre()+" y ladra diciendo: "+dogScruffy.says());
      System.out.println("Paco tiene otro perrito y dice que es igual que Spot o que Scruffy, vamos a comprobarlo");   
      System.out.println("Si comparamos a Spot con el perro de Paco utilizando ==: "+(dogSpot==dogNuevo));
      System.out.println("Si comparamos a Scruffy con el perro de Paco utilizando ==: "+(dogScruffy==dogNuevo));

      System.out.println("Si comparamos a Spot con el perro de Paco utilizando equals: "+dogNuevo.equals(dogSpot));
      System.out.println("Si comparamos a Scruffy con el perro de Paco utilizando equals: "+dogNuevo.equals(dogScruffy));
   }
}

Aquí el resultado es:
Tengo dos perritos.
Uno se llama: Spot y ladra diciendo: Ruff
El otro se llama: Scruffy y ladra diciendo: Wurf
Paco tiene otro perrito y dice que es igual que Spot o que 
Scruffy, vamos a comprobarlo
Si comparamos a Spot con el perro de Paco utilizando 
==: false
Si comparamos a Scruffy con el perro de Paco utilizando 
==: false
Si comparamos a Spot con el perro de Paco utilizando 
equals: false
Si comparamos a Scruffy con el perro de Paco utilizando 
equals: false

En las clases creadas por nosotros, el método equals() se comporta comparando referencias, es decir, como el operador ==, por tanto, para comparar dos objetos y comprobar si son iguales o no, deberíamos sobreescribir el método equals() para que tenga este comportamiento, esto lo veremos más adelante.
Vamos a darle otra vuelta de tuerca:
 public class Equivalencia4 {
   public static void main(String[] args) {
     Integer n1 = 47;
     Integer n2 = 47;
     Integer n3 = new Integer(47);
 
     System.out.println("n1==n2: "+(n1 == n2));
     System.out.println("n1!=n2: "+(n1 != n2));
     System.out.println("n1.equals(n2): "+n1.equals(n2));
     System.out.println("n1==n3: "+(n1 == n3));
     System.out.println("n1.equals(n3): "+n1.equals(n3));

     String s1 = "Hola";
     String s2 = "Hola";

     System.out.println("s1==s2: "+(s1 == s2));
     System.out.println("s1!=s2: "+(s1 != s2));
     System.out.println("s1.equals(s2): "+s1.equals(s2));
   }
} 

El resultado es:
n1==n2: true
n1!=n2: false
n1.equals(n2): true
n1==n3: false
n1.equals(n3): true
s1==s2: true
s1!=s2: false
s1.equals(s2): true

Cuando creamos n1 y le damos el valor 47, este valor se guarda en memoria (en la pila). Al crear una nueva variable con valor 47, en lugar de almacenar este valor en memoria, como ya existe, n2 apuntará al valor 47 que está en la pila. Entonces tenemos n1 y n2 apuntando a 47. Lo mismo ocurriría con s1 y s2. Por este motivo, en este ejemplo, las comparaciones tienen el mismo comportamiento para == (referencias) y equals() (valores). Sin embargo, si damos un nuevo valor a n1, n2, s1 o s2, las variables ya no apuntarán al mismo valor y serán de este modo diferentes, tanto para == como para equals(). Si nos fijamos en la variable n3, se crea con new Integer(47), si la comparamos con n1 o n2 el resultado es el siguiente: con == el resultado sería false, ya que al crear un nuevo objeto con new, se le asigna un nuevo espacio de memoria (en el cúmulo), por tanto no apuntaría al valor 47 de la pila, sin embargo, con equals() el comportamiento es el esperado ya que se comparan los valores y estos son iguales. Para recordar dónde se almacena cada tipo de variable, puedes repasar el Tema 2, el apartado Los lugares de almacenamiento.

30 comentarios:

  1. es muy poco aclaratorio lo que explicas, te complicas mucho la vida. por ejemplo no explicas que si comparas 2 strings, la comparacion es correcta porque la classe String tiene un mètodo equals, por tanto es logico que te de bien la comparacion de dos strings. en ningun momento enseñas como hacer correctamente el equals.
    en parte encuentro bien que la gente explique lo que sabe, pero no estoy deacuerdo a que se lie a la gente con explicaciones incompletas y mal hechas

    ResponderEliminar
    Respuestas
    1. Hola:
      No sé exactamente por qué escribí este artículo, creo que fue por algún problema que me encontré con el operador "==" y escribí este artículo por si alguien se encontraba el mismo problema.

      Cuando escribo un artículo, trato de documentarme al máximo y créeme, hasta que lo publico le doy mil vueltas, así que si piensas que mi explicación está incompleta o mal hecha, no ha sido por no informarme o no trabajar en lo que escribo.

      Si crees que está incompleto o mal explicado, desde aquí te animo a que tú mismo/a hagas un artículo que explique todo mejor y yo me comprometo a publicártelo.

      Un saludo.

      Eliminar
  2. No hagas caso, la envidia siempre lleva a ver errores donde no los hay, a mi me parecio bastante buena tu explicacion y con ejemplos sencillos. Saludos!

    ResponderEliminar
    Respuestas
    1. Gracias.
      Yo acepto las críticas malas, ya que seguro que me hacen mejorar, lo que pasa es que para alguien que hace un blog de manera desinteresada, que "pierde" parte de su tiempo libre en escribir para los demás, le desaniman un poco estos comentarios, pero bueno, también las críticas negativas son necesarias, no todo va a ser bueno ¿no?

      Un saludo y me alegro de que te sea útil el blog.

      Eliminar
  3. Me pareció bastante interesante :D

    ResponderEliminar
    Respuestas
    1. Muchas gracias, me alegro de que te haya sido útil.

      Un saludo.

      Eliminar
  4. Excelente artículo, no hagas caso de los malos comentarios

    ResponderEliminar
    Respuestas
    1. Muchas gracias, la verdad es que os estoy muy agradecida. Me alegro mucho de que mi trabajo os ayude a entender mejor Java.

      Saludos.

      Eliminar
  5. yo creo que quien comento desfaborablemente en primer lugar tiene razon y en segundo lugar tiene todo el derecho de emitir tal comentario, que no presenta tener mala voluntad sino contribuir con su real opinion ademas de haber sido concreto en lo tecnico y no personal como otros.

    ResponderEliminar
    Respuestas
    1. Hola:

      Está claro que no hay ni que dejarse llevar por los comentarios buenos y aprender de los malos, ya que eso me permitirá mejorar.
      Lo que tenéis que tener claro es que mejor o peor, escribo el blog tratando de ayudar a la gente y tratando de hacerlo lo mejor posible.
      Los comentarios negativos sólo me servirán para mejorar y pienso que son necesarios.

      Un saludo y gracias.

      Eliminar
  6. Respuestas
    1. De nada. Espero que te sea útil mi blog y te ayude a aprender.

      Saludos.

      Eliminar
  7. El artículo es bueno ya que aporta una demostración lógica completa del comportamiento de los operadores == y equals. De hecho el artículo se titula: "Diferencias entre método equals() y operador ==". Cómo reimplementar el método equals correctamente debería ocupar otro artículo.
    Mi enhorabuena por el Blog.

    ResponderEliminar
    Respuestas
    1. Muchas gracias, me alegro mucho de que te haya servido de ayuda, ese es el objetivo del blog.

      Saludos y suerte.

      Eliminar
  8. if (CBOListGasolina.getSelectedItem().equals("84 Octanos")) {
    Mostrar resultados - funciona

    }

    Pero si lo coloco dentro de un evento ItemStateChanged

    private void CBOListGasolinaItemStateChanged(java.awt.event.ItemEvent evt) {

    if (CBOListGasolina.getSelectedItem().equals("8d4 Octanos")) {
    ////No Funciona
    **Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
    at gui.Ventana.CBOListGasolinaItemStateChanged***
    }


    por que funciona en un metodo y en otro no???

    ResponderEliminar
    Respuestas
    1. Hola:

      Fíjate que dentro del método CBOListGasolinaItemStateChanged has puesto equals("8d4 Octanos") en lugar de equals("84 Octanos"), quizá de ahí viene el error.

      Un saludo.

      Eliminar
  9. Bien con el articulo, por la experiencia que tengo he tenido problemas con estos operadores ==, !=, y no siempre haces uso de los equals, por lo que hay q saber como utilizarlos.

    ResponderEliminar
    Respuestas
    1. Hay que tener claro la diferencia entre equals y estos operadores.

      Me alegro de que te haya gustado.

      Saludos.

      Eliminar
  10. Sigo sin saber cuando usar equals y cuando usar ==...

    ResponderEliminar
    Respuestas
    1. Te recomiendo que te leas en el tema 3 el apartado: Comprobación de la equivalencia de objetos http://piensaenjavadesdecero.blogspot.com.es/2011/02/operadores_01.html a ver si te ayuda a aclararte.
      equals se utiliza con objetos y los operadores == y != con primitivas.

      Saludos.

      Eliminar
  11. Hola, no entiendo por qué v1.i = v2.i = 100 y v1.equals(v2) es false si a ambos objetos se les asigna el mismo valor a i

    ResponderEliminar
    Respuestas
    1. Hola:

      Respondo a tu pregunta.

      En las clases creadas por nosotros, el método equals() se comporta comparando referencias, es decir, como el operador ==, por tanto, para comparar dos objetos y comprobar si son iguales o no, deberíamos sobreescribir el método equals() para que tenga este comportamiento, eso lo veremos más adelante.

      Espero haberte ayudado.

      Un saludo y gracias.

      Eliminar
  12. Muy buena la info, si quieres de doy dominio y hosting.
    "da te vid @ gmail . com"
    Saludos

    ResponderEliminar
  13. Este comentario ha sido eliminado por el autor.

    ResponderEliminar
  14. Buenas! me parece una buena explicación y me gustaría dar mi opinión al respecto (espero no meter la pata jeje) :

    El operador == comprueba si los elementos a comparar tienen el mismo valor. En el caso de referencias a objetos, comprueba que las referencia sea la misma, es decir, que apunten a la misma zona de memoria (que viene a decir que apunten al mismo objeto).

    Cuando tu creas una clase, deriva de la clase Object que cuya implementación del metodo equals da verdadero si los objectos a comparar son los mismos (es decir, como el ==) por eso tienes que sobreescribir el metodo equals para establecer cual es el criterio de igualdad para tu clase.

    Clases ya hechas como String o Integer, pertenecientes a la api de java, ya tienen sobreescrito su método equals.

    ResponderEliminar
    Respuestas
    1. Hola Justo:

      Muy buena aportación y bien explicado. Seguro que ayuda a mucha gente a entenderlo mejor.

      Saludos y muchas gracias.

      Eliminar
  15. Muy buen aporte, se agradece este post.
    Muchas gracias y un saludo.

    ResponderEliminar
    Respuestas
    1. Muchas gracias. Me alegro que te sirva de ayuda.

      Saludos.

      Eliminar
  16. Excelente articulo, de mucha utilidad para mi, gracias.

    ResponderEliminar
  17. Muy bueno, gracias por compartir :)

    ResponderEliminar