• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Tim Cooke
  • Ron McLeod
  • paul wheaton
  • Jeanne Boyarsky
Sheriffs:
  • Paul Clapham
  • Devaka Cooray
Saloon Keepers:
  • Tim Holloway
  • Roland Mueller
  • Himai Minh
Bartenders:

equals method in java

 
Greenhorn
Posts: 10
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
We generally override hashCode() whenever we need to store objects in collection based on hashCode values. Based on the logic objects with same hashCode will store under same bucket. This is OK.
But a contract in java says whenever you override hashCode method you must override equals method too.
When i add an object using add method it calculates the hashCode value and stores the object. Here overriding hashCode method helpful in storing but when will this equals method useful in collections?[size=9]
[/size]
 
Bartender
Posts: 6109
6
Android IntelliJ IDE Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
The hashcode only tells us what bucket the object will be in if it's there. We still need equals() to compare the objects in that bucket to see if any of them are the one we're looking for.

HashCode() and equal() are so often used together that you should always override both whenever you're overriding one. There's no good reason not to, and generally it's required.
 
divya chowdhary
Greenhorn
Posts: 10
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
So hashCode method is helpful in storing the objects where as equals method helps in retrieving objects from the collection. Am i right?
Also the usage of these methods will remain same in all hash based collections or will these functionality differ from collection to collection?
 
Marshal
Posts: 80765
488
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

divya chowdhary wrote: . . . But a contract in java says whenever you override hashCode method you must override equals method too. . . .

No, it doesn’t. It says that if you override equals you must override hashCode too. That is the other way round. It is probably a good idea to override both together all the time, but the contract permits you to override hash and not override equals.
 
Jeff Verdegan
Bartender
Posts: 6109
6
Android IntelliJ IDE Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

divya chowdhary wrote:So hashCode method is helpful in storing the objects where as equals method helps in retrieving objects from the collection. Am i right?



hashCode() is used for storing (to determine which bucket to put it into) and for retrieving (for determining which bucket to look for it in).

equals() is used for storing (if the collection doesn't allow duplicates, to see if an equivalent object is already present within the bucket that hashCode() selected) and for retrieving (to find the specific object we're looking for in the bucket selected by hashCode().

 
Bartender
Posts: 10780
71
Hibernate Eclipse IDE Ubuntu
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

divya chowdhary wrote:But a contract in java says whenever you override hashCode method you must override equals method too.


Basically, the two work together, for all the reasons already given. Either don't override either, or override both; otherwise you're setting yourself up for a pile of unnecessary documentation.

Winston
 
divya chowdhary
Greenhorn
Posts: 10
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Campbell Ritchie wrote:

divya chowdhary wrote: . . . But a contract in java says whenever you override hashCode method you must override equals method too. . . .

No, it doesn’t. It says that if you override equals you must override hashCode too. That is the other way round. It is probably a good idea to override both together all the time, but the contract permits you to override hash and not override equals.



In the java docs put method was defined as
"Associates the specified value with the specified key in this map. If the map previously contained a mapping for this key, the old value is replaced."

I have developed a sample program to check this functionality. here i have just override hashCode method but not equals. So it considers the default equals method implementation and stores the object. As a result two objects with same key is stored which is a meaningless. But when i override both methods the statement what was said in java docs getting true. How is it behaving? Is there any wrong in my knowledge or put method works properly according to specification only when we override both the methods?

Also why it is saying that c1.equals(c3) is false even though the hashcode is same for both objects when i won't override equals.

import java.util.*;
class Maps
{
public static void main(String[] args)
{
Car c1= new Car("honda");
Car c2= new Car("bmw");
Car c3= new Car("honda");
Person p1= new Person("a");
Person p2= new Person("b");
Person p3= new Person("c");

HashMap<Car, Person> hs = new HashMap<Car, Person>();
hs.put(c1,p1);
hs.put(c2,p2);
hs.put(c3,p3);
System.out.println(hs.size());
Set s = hs.keySet();
Iterator itr= s.iterator();
while(itr.hasNext()){
Object key= itr.next();
Object value= hs.get(key);
System.out.println(((Car)key).name+" "+((Person)value).name);
}
System.out.println(c1.equals(c3));
}
}
class Car
{
String name;
Car(String name){
this.name=name;
}
public int hashCode(){
return 9+(this.name.length());}
public boolean equals(Object obj){
if(this.name.equals(((Car)obj).name))
return true;
else
return false;
}
};
class Person
{
String name;
Person(String name){
this.name=name;
}

};
 
Campbell Ritchie
Marshal
Posts: 80765
488
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

divya chowdhary wrote: . . .
In the java docs put method was defined as’t over
"Associates the specified value with the specified key in this map. If the map previously contained a mapping for this key, the old value is replaced."

Which is totally different from what I said earlier.



I have developed a sample program to check this functionality. here i have just override hashCode method but not equals. So it considers the default equals method implementation and stores the object. As a result two objects with same key is stored which is a meaningless.

No, it isn’t. That is exactly how you have designed your class. Put all my instances as keys in the same bucket but we all get different values. It is behaving exactly as the documentation says. Because the equals method returns false, the put method interprets that as two different keys.

But when i override both methods the statement what was said in java docs getting true. . . .

Also why it is saying that c1.equals(c3) is false even though the hashcode is same for both objects when i won't override equals. . . .

The map class behaved exactly as the documentation said in all instances. When you don’t override the equals() method, but do override hash, the map sees them as looking the same, but not being the same. Like twins. The map sees them as different objects. So does equals(). If you don’t override it, equals uses return other == this; or similar. If you override both, then they cease to be twins. They come along to the map saying, “We are not twins, we don’t simply look the same, we are both the same.” If you wrote the class, then you define what “same” means, in the equals method.

If you really want to see things go wrong, try overriding equals and not overriding hash

By the way, that equals method in Car does not fulfil the requirements for the general contract, because it will not accept every kind of Object without any Exceptions.
 
divya chowdhary
Greenhorn
Posts: 10
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
In my last example both c1, c3 objects have same hash code values but why is it showing the result "false" when comparing "c1==c3", as the "==" returns true when both values are same?

Finally what is the big mistake in not overriding hashCode method when we override equals method? Is the only reason behind this is the bad design of storing equal objects in different buckets or any performance issues
 
Ranch Hand
Posts: 1164
Eclipse IDE Firefox Browser Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

divya chowdhary wrote:In my last example both c1, c3 objects have same hash code values but why is it showing the result "false" when comparing "c1==c3", as the "==" returns true when both values are same?



== test returns true only when both the references point to the same object, not the same value. Try this:



See the difference for yourself. What do the statements print and why?
 
Mansukhdeep Thind
Ranch Hand
Posts: 1164
Eclipse IDE Firefox Browser Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Divya wrote: Finally what is the big mistake in not overriding hashCode method when we override equals method? Is the only reason behind this is the bad design of storing equal objects in different buckets or any performance issues



Well, there are lots of issues that you may run into when you override public boolean equals(Object object) without overriding public int hashCode() of the java.lang.Object class.

But for understanding the gravity of the reasons of sticking to this equals() hashCode() contract, you have to understand the concepts of equality and hash code separately first. Later I will merge the two concepts with an example to answer your query, or rather lead you to the answer. But first , observe what happens when you run the above code. Why is that happening?
 
Campbell Ritchie
Marshal
Posts: 80765
488
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
That is not a good example; if Integer objects are cached up to 0x0400 that program will behave differently from the minimum size of cache.
 
divya chowdhary
Greenhorn
Posts: 10
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
@Mansukhdeep Thind

First of all thanks for your explanation in clarifying me the working of "==". With the earlier explanations from "Jeff Verdegan, Campbell Ritchie" i have understood the functionality of these two methods and how the functionality will be when we override hashCode method but not equals. But still i have some interest in knowing the problems that lies behind overriding equals but not hashCode. That's why i have posted it as my last question...
 
Mansukhdeep Thind
Ranch Hand
Posts: 1164
Eclipse IDE Firefox Browser Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Campbell Ritchie wrote:That is not a good example; if Integer objects are cached up to 0x0400 that program will behave differently from the minimum size of cache.



Yes, I know that Ritchie. Let me take her through with her doubt first. Then I will tell her about this unique behavior too.
 
Campbell Ritchie
Marshal
Posts: 80765
488
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
It is easy enough to try out your Car class by commenting out the hash code method.
 
Mansukhdeep Thind
Ranch Hand
Posts: 1164
Eclipse IDE Firefox Browser Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Now observe carefully:



I put the (c1,p1) pair in the map. Yet, when I am trying to retrieve it using an equal object, I am unable to find person name divya. What just happened?
 
Campbell Ritchie
Marshal
Posts: 80765
488
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Apart from the fact that your equals() method is written incorrectly?
 
Mansukhdeep Thind
Ranch Hand
Posts: 1164
Eclipse IDE Firefox Browser Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Campbell Ritchie wrote:Apart from the fact that your equals() method is written incorrectly?



What's wrong with my implementation?
 
Sheriff
Posts: 28413
102
Eclipse IDE Firefox Browser MySQL Database
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
You made the common beginner's mistake of using == to compare two Strings for equality of contents instead of the equals() method.
 
Mansukhdeep Thind
Ranch Hand
Posts: 1164
Eclipse IDE Firefox Browser Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Paul Clapham wrote:You made the common beginner's mistake of using == to compare two Strings for equality of contents instead of the equals() method.





OK Now Paul? Or is there something still missing?
 
Jeff Verdegan
Bartender
Posts: 6109
6
Android IntelliJ IDE Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Mansukhdeep Thind wrote:
OK Now Paul? Or is there something still missing?



You made it worse.

You still have the error of using == to compare objects where you should use equals(), and you broke and then removed an optimization at the beginning of the method.



Changing that == to equals() was a mistake, the discovery of which is what I assume led you to comment it out. You should have left it as-is. That's one case where you do want to use ==.
 
Winston Gutkowski
Bartender
Posts: 10780
71
Hibernate Eclipse IDE Ubuntu
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Mansukhdeep Thind wrote:OK Now Paul? Or is there something still missing?


Well, apart from what Jeff told you, my only other observation is that an equals method like that should be defined as final (at least to start with).

Winston
 
Campbell Ritchie
Marshal
Posts: 80765
488
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
There are lots of ways to write a correct equals method.That version has the advantage that you are not using multiple return. It still has the vulnerability that it will suffer an Exception if either of the fields in the obj object points to null.

Three references: (1) Joshua Bloch Effective Java™, (2) Angelika Langer’s Java FAQ website about equals and hash code, (3) Odersky Spoon and Venners: see this FAQ.
 
Mansukhdeep Thind
Ranch Hand
Posts: 1164
Eclipse IDE Firefox Browser Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Jeff Verdegan wrote:

Mansukhdeep Thind wrote:
OK Now Paul? Or is there something still missing?



You made it worse.

You still have the error of using == to compare objects where you should use equals(), and you broke and then removed an optimization at the beginning of the method.



Changing that == to equals() was a mistake, the discovery of which is what I assume led you to comment it out. You should have left it as-is. That's one case where you do want to use ==.



No Jeff, when Paul said that I need to use equals() to compare 2 String objects, I thought he was referring to the first if check and not the instance variable comparisons. I referred to Joshua's recipe only. Here is the updated method:


Campbell wrote: That version has the advantage that you are not using multiple return. It still has the vulnerability that it will suffer an Exception if either of the fields in the obj object points to null.



I prefer writing it in multiple lines. Makes the code easier to read.
 
Campbell Ritchie
Marshal
Posts: 80765
488
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
You do realise that using instanceof can cause the method to lose symmetry if the class is subclassed? Winston G’s technique of marking everything final will preserve symmetry,
 
Jeff Verdegan
Bartender
Posts: 6109
6
Android IntelliJ IDE Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Mansukhdeep Thind wrote:



That null test is pointless. If it's null, instanceof will evaluate to false.



This is still subject to throwing NPE if name or color are allowed to be null.
 
Mansukhdeep Thind
Ranch Hand
Posts: 1164
Eclipse IDE Firefox Browser Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Campbell Ritchie wrote:You do realise that using instanceof can cause the method to lose symmetry if the class is subclassed? Winston G’s technique of marking everything final will preserve symmetry,



What do you mean when you say that using instanceof can cause the method to lose symmetry if I have a child class that extends Cars? what is meant by method symmetry?
 
Jeff Verdegan
Bartender
Posts: 6109
6
Android IntelliJ IDE Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Mansukhdeep Thind wrote:

Campbell Ritchie wrote:You do realise that using instanceof can cause the method to lose symmetry if the class is subclassed? Winston G’s technique of marking everything final will preserve symmetry,



What do you mean when you say that using instanceof can cause the method to lose symmetry if I have a child class that extends Cars? what is meant by method symmetry?



As explained in the javadocs for equals(), x.equals(y) must return the same value as y.equals(x) for all non-null x and y.

If you create a class SportsCar extends Car, and SportsCar's equals() method tests for instanceof SportsCar, then Car.equals(SportsCar) can return true, but SportsCar.equals(Car) can only return false. Therefore, symmetry can be violated.
 
Mansukhdeep Thind
Ranch Hand
Posts: 1164
Eclipse IDE Firefox Browser Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Jeff Verdegan wrote:

Mansukhdeep Thind wrote:



That null test is pointless. If it's null, instanceof will evaluate to false.



Roger that.

Jeff Verdegan wrote:

This is still subject to throwing NPE if name or color are allowed to be null.



How to overcome this? Do I put a null check in the constructor before initializing them or throw a NPE if any of these is a null value inside the equals method?
 
Mansukhdeep Thind
Ranch Hand
Posts: 1164
Eclipse IDE Firefox Browser Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Jeff Verdegan wrote:

Mansukhdeep Thind wrote:

Campbell Ritchie wrote:You do realise that using instanceof can cause the method to lose symmetry if the class is subclassed? Winston G’s technique of marking everything final will preserve symmetry,



What do you mean when you say that using instanceof can cause the method to lose symmetry if I have a child class that extends Cars? what is meant by method symmetry?



As explained in the javadocs for equals(), x.equals(y) must return the same value as y.equals(x) for all non-null x and y.

If you create a class SportsCar extends Car, and SportsCar's equals() method tests for instanceof SportsCar, then Car.equals(SportsCar) can return true, but SportsCar.equals(Car) can only return false. Therefore, symmetry can be violated.



Is marking it as final the only way to prevent that problem? Or is there another way too?
 
Campbell Ritchie
Marshal
Posts: 80765
488
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Yes. I know of two ways:-
You can mark the class final, which is probably better.
You can use getClass instead of instanceof.
 
Jeff Verdegan
Bartender
Posts: 6109
6
Android IntelliJ IDE Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Mansukhdeep Thind wrote:

Jeff Verdegan wrote:

This is still subject to throwing NPE if name or color are allowed to be null.



How to overcome this? Do I put a null check in the constructor before initializing them



That's one way. But you don't do that to fix equals(). You do it if your design dictates that those fields cannot be null.

or throw a NPE if any of these is a null value inside the equals method?



No, equals should not throw any excpetions. And throwing an NPE to avoid an NPE is not particularly productive. If the semantics of your object allow those fields to be null, then, obviously, you have to test for them being null in equals() before you dereference them.
 
Jeff Verdegan
Bartender
Posts: 6109
6
Android IntelliJ IDE Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Mansukhdeep Thind wrote:

Jeff Verdegan wrote:
If you create a class SportsCar extends Car, and SportsCar's equals() method tests for instanceof SportsCar, then Car.equals(SportsCar) can return true, but SportsCar.equals(Car) can only return false. Therefore, symmetry can be violated.



Is marking it as final the only way to prevent that problem? Or is there another way too?



You can mark the method final.

Or you can document that Car uses instanceof Car, and therefore every subtype must also use instanceof Car if it overrides equals().

Or you can use getClass() instead (as long as no supertype of Car uses instanceof).

As with so many other things, which approach is "best" depends on the semantics of your class. For instance, classes in the Collections Framework have to use instnaceof List/Set/Map. But for some other classes, it might never make sense for an instance of a subclass to be equal to an instance of a parent class, so you'd use getClass().
 
Mansukhdeep Thind
Ranch Hand
Posts: 1164
Eclipse IDE Firefox Browser Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I think I shall stick to Winston's advice for now. I will mark it as final so that there is no chance of the symmetry being ruptured. And as for the null value problem, let us assume that the car name and color can not be null. So, here it is:



Makes sense now?
 
Winston Gutkowski
Bartender
Posts: 10780
71
Hibernate Eclipse IDE Ubuntu
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Mansukhdeep Thind wrote:I think I shall stick to Winston's advice for now.


Fine, but do read the links that Campbell gave you. As Jeff said, the way you write equals() depends entirely on how you want it to work; there is no 'right' or 'wrong' about it (well actually, there are plenty of ways to write it wrong, but that's outside what we're talking about here ); final is just a way of ensuring that nobody can accidentally foul up your equals() contract.

Another way of ensuring symmetry is with a canEqual() method (explanation is near the bottom of the page). Yet another thing worth knowing about.

Winston
 
Winston Gutkowski
Bartender
Posts: 10780
71
Hibernate Eclipse IDE Ubuntu
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Mansukhdeep Thind wrote:I think I shall stick to Winston's advice for now.


And, at the risk of muddying the waters even more, I like to add an extra typed equalTo() method, viz:then your equals() method becomes:and you can pretty much cookie-cutter it for any subclass (changing 'ThisClass' as appropriate of course).

Winston
 
Mansukhdeep Thind
Ranch Hand
Posts: 1164
Eclipse IDE Firefox Browser Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I don't think Divya is coming back with any more of equals() / hashCode() doubts when she reads all this. Or may be she will. Anyways, let's see if she has understood the importance of equal objects should have equal hash code rule which is why I wrote the code in the first place.
 
Winston Gutkowski
Bartender
Posts: 10780
71
Hibernate Eclipse IDE Ubuntu
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Mansukhdeep Thind wrote:I don't think Divya is coming back with any more of equals() / hashCode() doubts when she reads all this...let's see if she has understood the importance of equal objects should have equal hash code rule which is why I wrote the code in the first place.


Agreed. Fun stuff though. Who'd have thought that something that seems so simple could end up being such a PITA?

Winston
 
Consider Paul's rocket mass heater.
reply
    Bookmark Topic Watch Topic
  • New Topic