I have a hashcode implementation for a class and the hashcode implementation is consistent with what eclipse generates and also the most commonly accepted practice as discussed here
Here is my hashcode implementation(All the Ids used in this method constitute the key for the object):
public int hashCode() {
final int prime = 31;
int hashCode = 1;
if(uId != null){
hashCode = prime * hashCode + uId.hashCode();
}
if(rId != null){
hashCode = prime * hashCode + rId.hashCode();
}
if(bId != null){
hashCode = prime * hashCode + bId.hashCode();
}
if(reId != null){
hashCode = prime * hashCode + reId.hashCode();
}
if(cId != null){
hashCode = prime * hashCode + cId.hashCode();
}
return hashCode;
}
I ran into a scenario where I was testing with a very large data set and my collection did not have the expected number of objects of this class. On looking closely the below two data sets resulted in the same hashcode : 50268236873 and hence a record was being replaced by the last one that was added to the collection as their hashcodes were same.
Existing record :
Record@2c0781cd[uId=54046,rId=10967,bId=177,reId=1728,cId=50194]
Record being inserted into the collection :
Record@20dad050[uId=53806,rId=18389,bId=177,reId=19026,cId=50194]
Both of these had the hashCode value = 50268236873
So, the questions :
1] This is clear case where hash codes of two different objects have the same value. So how to ensure this does not happen with any data set ? Should the prime be larger ?
2] If we look closely the hashCode variable in the implementation is of int data type whose largest value is 2^31 - 1 = 2147483647 which is greater that the hashcode that is computed for the above data set = 50268236873, so there is a overflow. Is there any consequence to use long as the type of hashCode value ?
thanks
Nohsib
Edit :
I am using HashSet and after reading through the answers posted, I looked up the equals implementation,as below, and I think because in the equals I check to see if the hashCodes of the two objects are same and use that to determine if they are same objects is leading to this issue.
Can any of you guys confirm this ?
@Override
public boolean equals(Object paramObject) {
boolean equals = false;
if (paramObject != null) {
ACRecord other = (ACRecord) paramObject;
if ((this.hashCode() == other.hashCode()) // I think this is where I am going wrong
|| (this.uId.equals(other.getUId())
&& this.rId.equals(other.getRId())
&& this.reId.equals(other.getReId())
&& this.bId.equals(other.getBId())
&& this.cId.equals(other.getCId))) {
equals = true;
}
}
return equals;
}
Solution : My equals method implementation was wrong since I used hashCode to determine if two objects were equal.Correcting the equals method implementation resolved my issue were hashset was replacing an exisintg record.
Typically, hash codes don't guarantee uniqueness. HashMap implementations typically deal with collisions by storing a list behind the scenes, but they include a check that ensures that you don't get everything in the list as a match, just the ones that really match.
In other words, if you do map.get("foo") and there are collisions, the hash map will check each result (unhashed) to see if it really matches "foo". Then it returns only exact matches.
Note also that while the contract for hashcodes states that any two objects that respond true to equals() should have the same hashcode, the opposite is not necessarily true.