Behavior of different ways of comparison in Java

Asked

Viewed 930 times

16

If for example I have the code below, I am comparing the reference of the objects in the case ex1 and ex2 and not the object itself, correct?

Pessoa ex1 = new Pessoa();
Pessoa ex2 = new Pessoa();
if(ex1 == ex2)
{
    System.out.println("Igual");
}
else
{
    System.out.println("não é igual");
}

Here it is always printed that is not equal, so I believe that I am comparing correct references?


My other question is whether this matches the code above that was typed:

Pessoa ex1 = new Pessoa();
Pessoa ex2 = new Pessoa();
if(ex1.equals(ex2))
{
    System.out.println("Igual");
}
else
{
    System.out.println("não é igual");
}
  • 4

    Sorry about the "ad" but I wrote a post about it on the blog. It is a bit extensive to be ported in full here: http://a.accioly.7rtc.com/2011/05/java-vs-equals.html

4 answers

12

Answer :

Comparison using : "==" versus "equals"

What is the Heap?

Heap is a reserved and protected place of the JVM (Java Virtual Machine) where are allocated all instantiated objects during the execution of its program... each of these objects has a memory address where it is stored, and the only access to it, is via a reference (stored outside the heap).... This reference also has the memory address, making your access unique and exclusive to this object.

When you use the operator "==" to compare, you are comparing external to heap already the comparison using "equals" would be the inverse of the operator "==" which compares the objects within the heap, ie their characteristics.

In short:

"==" ----> is used to compare two objects, checking whether objects refer to the same place in memory.
"equals" ---> this method is defined in the Object class, from which all classes are either a direct or indirect descendant. By default the method equals () behaves the same as the operator "==" but, the class String replaces (overwrites) the equals() method for comparing characters in a string and not their locations in memory.

Observing:

Besides this is ideal when we create a new object, of type for example, Person(as you created), override the method "equals" inherited from Object, so we can see if people are really equal. An example would be if your class Person possess the attributes age and name you can overwrite the method equals thus:

public boolean equals(Pessoa p) {  

      boolean igual = false;  
      if ((this.nome.equals(p.getNome())) && (this.idade == p.getIdade()))  
         igual = true;  

      return igual;  
   }  

Research references:

Link Programmer Interview
Link Stack Overflow English
Link Javafree

8

As for the first question, you are correct.

Second:

Not necessarily. In Java the method Equals() always looking for (or at least should, depends on the actual implementation) find the most descriptive comparison. You can even in some cases compare the reference, when this is the most descriptive way of comparing objects. But in principle the reference comparison is avoided, when possible.

The best example is the comparison of strings. Two strings identical but at different addresses return true when using Equals but false when using == (That’s a little more complicated than this because of interning, but this is another matter).

So for your specific case, it would be the same thing. Unless the implementation of Equals class specific Pessoa change the default behavior.

By default Java, through the class Object where all other classes are derived directly or indirectly, has an implementation of the Equals comparing the references.

But it would be fitting that this class Pessoa superimposed the standard implementation with another that was more descriptive. To take some (s) key(s) field(s) and compare them to determine whether it is the same identity or not. These fields would need to form a single key, i.e., no other instance of the class could be equal to another.

Is there this implementation? Only you can see it, we are not seeing it. You have to see in this class whether there is this implementation and how it is proceeding.

In the comment from Anthony Acciolly above has a link to his blog with a good example of implementing using CPF as the person’s unique identity identifier. It’s a good way to make the Equals class Pessoa more relevant. Note that there is nothing extraordinarily wrong with comparing references in this case. But it is not the most intuitive. The ideal is to make this comparison have a semantics of its own. See how it did the implementation of Equals for the class thinking about the various situations of the object:

@Override
public boolean equals(Object obj) {
    // Um objeto é sempre igual a ele mesmo
    if (this == obj) {
        return true; 
    }
    // Um objeto nunca deve ser igual a null
    if (obj == null) {
        return false;
    }
    /* Uma pessoa só pode ser igual a outra pessoa.
     * Caso uma pessoa possa ser igual a uma de suas subclasses
     * use !(obj instanceof Pessoa)
     */
    if (getClass() != obj.getClass()) {
        return false;
    }
    // Converte a referencia para uma pessoa
    final Pessoa other = (Pessoa) obj;
    // Duas pessoas só podem ser comparadas se possuem CPF
    if (this.cpf == null || other.cpf == null) {
        return false;
    }        
    // Duas pessoas são iguais se possuem o mesmo CPF
    return this.cpf.equals(other.cpf);
}

I put in the Github for future reference.

See the implementation details on his blog (he kindly provided this example).

I talk about it in more detail in that reply. It is about C# and has some differences for Java, but there I explain better this identity thing. I also speak a little identity here in that reply.

  • 1

    I have two small criticisms regarding this example of equals: 1) If a person does not have a CPF, they will be considered unlike herself... Help in the beginning of equals put a "quick test" if( this == obj ), that not only prevents cases like this but potentially improves overall performance. 2) When comparing classes, this code is closing this object to the possibility of extension - no one could for example create a subclass of Pessoa that could still be compared to her for equality. In my opinion, it would be preferable to do if( !(obj instanceof Pessoa) ).

  • @mgibsonbr the example is not mine :P I agree with all the points raised. Of course it lacks a little context. It may be that if you do not have a social security number you are an invalid, then a comparison should always be false. Maybe it should be an exception if it were, but I don’t know, it’s just speculation. Maybe the intention is that the class shouldn’t be extended.

  • 1

    @mgibsonbr. On the comparison with the reference itself I agree with you (and indeed, some IDES generate the code in this way). As for closing the possibility of extension, this was purposeful, you do not always want an object in a subclass to be equal to the object in the class itself.

  • Isn’t this site fantastic? We all learn a little more.

  • 1

    @Anthonyaccioly I imagined it was purposeful, but wanted to call attention to the fact anyway. There are pros and cons to doing it that way, I think it would be worth a separate question on the subject... P

  • 1

    Just to mention something I forgot on the blog. So much Guava how much Apache Commons have facilitators to implement the method equals.

  • 1

    @mgibsonbr, sorry to disturb the comments but I updated the example in the blog as your suggestions (I used to link to ;). If you want to update the example here feel free.

  • @Anthonyaccioly is not tumultuando anything not, on the contrary, is contributing positively.

Show 3 more comments

7

By default, any class that does not declare extension of another class, under the cloths, it will extend java.lang.Object. In your Person object, since you have not overwritten the equals method that he inherited from java.lang.Object, comparison will be made like this (source code of equals method java.lang.Object):

public boolean equals(Object obj) {
    return (this == obj);
}

If you want to change the behavior of equals (which is perfectly normal to obey a possible entity’s contract for example), we would usually do (briefly):

public boolean equals(Object obj) {
    return this.id == ((Pessoa) obj).id;
}

Here you would be saying that one Person is only like another IF the id is equal.

1

"Here it is always printed that is not equal, so I believe that I am comparing correct references?"

Correct.

"My other question is whether this matches the code above that was typed"

It is not the same. You yourself can define what is the criterion that makes two people "equal" and implement this criterion in the method public boolean equals(Object o) of the same class which, with the help of the annotation @Override, will overwrite the inherited class method Object. Example: two people are equal if their CPF is equal. There are some criteria that should be followed (contracts), mainly with the method compareTo of the interface Comparable. I advise to take a look at these contracts as well, mainly to follow community standards and make your code more robust.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.