Skip to content

Instantly share code, notes, and snippets.

@squarepegsys
Created December 1, 2011 01:25
Show Gist options
  • Save squarepegsys/1412556 to your computer and use it in GitHub Desktop.
Save squarepegsys/1412556 to your computer and use it in GitHub Desktop.
Some thoughts on .hashCode and .equals in Java

Thoughts about .equals and .hashCode

Here is a little context: we were tracking down a memory leak in our application and saw this big HashMap in the dump file. The HashMap was from ehcache. The problem was that the objects that were used as the key did not implement .hashCode. Oops. We used Eclipse's generated .equals and .hashCode to get us by and that got us through the heat of the moment.

So, today, a couple of developers were on a PMD/FindBugs hunt and, when I got home from work, I had an email from one of them. Seems that there was a debate on if using the generated hashCode and equals was "good enough" or if something else should be done. They asked me to weigh in on the matter. So here I am -- spouting off something that may or may not be a good idea.

I think that .equals and .hashCode should be generated with care. If you hit the IDE's "generate hashCode and equals" button, you should figure out what properties in that object make it unique. Maybe something like "name" should be involved. To me, certainly something like "id" should not be (who cares if it comes from the database?). But it depends on what your use case are.

And they are related -- the objects that makes sense to decide equality should also be included in generating a hashCode. So much so that if

 x.equals(y)==true 

then

 x.hashCode()==y.hashCode()

See an interesting discussion on StackOverflow about this. See if you agree.

So if they are truly related to each other in such a strong way, they should be implemented identically -- in accordance to the DRY Principal.

Therefore, this is how I propose the Most Correct way to implement .hashCode and .equals:

  1. Implement .hashCode as tight and as correct as possible.
  2. .equals simply becomes : return this.hashCode()==that.hashCode()

That way, the relationship between .hashCode and .equals will always exist. Ff you add properties that effects equality, then you simply have to change the .hashCode method -- the .equals will use that in it's decision.

@mkolakow
Copy link

mkolakow commented Dec 1, 2011

Also - I actually like your conclusion. It's a simpler. In fact, I'm curious if there is any good reason not to implement .equals in terms of .hashCode. And if there isn't -- why didn't Josh Bloch catch this one? ;) This seems to me a great application of DRY.

@squarepegsys
Copy link
Author

I fixed the link.

I'm a simple guy and really like simple things. I could see that two objects would have the same hashCode that should not be equal, but I think in 99% of the applications, that those two objects would appear at the same time within the app is minimal.

@adammartin
Copy link

The frustration I have with the auto generation is that it does not necessarily conform to the equals and hash code contracts. So few \people are willing to actually sit there and read what the contracts say you SHOULD do and then wonder why they don't work (and by the way the failures can be very difficult to track down).

Heaven forbid people were to actually TDD their way to equals and hash code. It's one of the very few occasions that you have a very clear acceptance criteria that applies at the unit level!!!!

Please people read the contract and write the tests ... your bug level will go drastically down.

By the way here is a link on the contracts (not as good as Josh Bloch's explanation but pretty much the same info):

http://www.technofundo.com/tech/java/equalhash.html

@mkolakow
Copy link

mkolakow commented Dec 1, 2011

On the other hand -- You've got me thinking. If there is even any remote possibility that two different objects could have the same hashcode, then this is simply not worth the risk. Essentially you are opening the door to an extremely nasty and hard to track down bug. Sure, unlikely to happen, but if it did, then you are potentially very screwed.

The Guava implementation is simply as follows:

for (Object element : a)
    result = 31 * result + (element == null ? 0 : element.hashCode());

Also - for what it's worth, see this discussion on StackOverflow

http://stackoverflow.com/questions/4360035/why-hashcode-can-return-the-same-value-for-different-objects-in-java

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment