package com.bhoopendra.example;
public class Test { private int num = 0; private String str = null; Test(String str, int num) { this.str = str; this.num = num; } @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (obj == null || obj.getClass() != this.getClass()) { return false; } Test obj2 = (Test) obj; return (this.num == obj2.num && this.str.equals(obj2.str)); } @Override public int hashCode() { int hash = 7 ; int result = 31*hash + num; result = 31* result + (str ==null ?0: str.hashCode()); return result; } } |
Lets take a simple class example.
Now let's take hashcode method first. As we all know that one has to override hashcode method along with equals method. The reason being simple .I am quoting Joshua Bloch . He says "
You must override hashCode in every class that overrides equals. Failure to do so will result in a violation of the general contract for Object.hashCode, which will prevent your class from functioning properly in conjunction with all hash-based collections, including HashMap, HashSet, and Hashtable."
Lets summarize the contract from Java specification :-
integer result.
It is not required that if two objects are unequal according to the equals(Object)
method, then calling the hashcode method on each of the two objects
must produce distinct integer results. However, the programmer should be
aware that producing distinct integer results for unequal objects may improve
the performance of hash tables
A good hash functions should return unequal hashcode for unequal objects. Ideally , a hash function should distribute any reasonable collection of unequal instances uniformally across all possible values.
Now lets analyze our hashcode finction for each line :
@Override
public int hashCode() {
int hash = 7 ;
int result = 31*hash + num;
result = 31* result + (str ==null ?0: str.hashCode());
return result;
}
Lets look at the line which is highlighted
int hash =7;
Idea behind using this non zero initialization is to affect hashvalue which would have rather unaffected any or all of the subsequent steps after this step and before returning any value have result in zero. Surely , more common hashcode values. Hence it would increase the chance of hash code collision. You could have taken 7 or 17. I would prefer prime number here. Now let's come to mutliplication factor . why 31 ? and why no 2, 4 ,6 10 etc. 31 is chosen because it is an odd prime.If it were even and the multiplication overflowed, the information would be lost, as multiplication by 2 is equivalent to shifting. Advantage of using 31 is less clear, but it is done traditionally. A nice property of 31 is that multiplication can be replaced by shift and subtraction for better performance.
31*i = i << 5 -i;
Modern VMs do this sort of optimisation automatically.
Now, I am presenting here some tips to write hashcode method which uses different types of data types.
Step-1 : Store some non zero constant value in some variable say, result.
e.g int result = 17;
Step-2 : For each significant field f ( all those attributes which you want to take into account ) in your object compute hash code.
2.a. If f is boolean
result += 31 * (f ?1:0) + result;
2.b. If f is byte, char, short or int , compute (int) f;
result += 31 * ((int) f);
2.c. If f is long, compute (int ) (f ^(f >>>32));
result += 31 * (int ) (f ^(f >>>32));
2.d. If is float , compute Float.floatToIntBits(f)
result += 31 * Float.floatToIntBits(f);
2.e. If f is double , compute Double.doubleToLongBits(f) and then hash the resulting long according to step 2.c
long x = Double.doubleToLongBits(f);
result += 31 * (int ) (x^(x>>>32));
2.f. If field is an array, treat it as if each element were a separate field and then compute hash code of each element by applying above rules recursively. Either way one can also use Arrays.hashcode method.
2.g. If the field is an object reference and this class’s equals method compares the field by recursively invoking equals, recursively invoke on the field. If a more complex comparison is required, compute a “canonical representation” for this field and invoke hashCode on the canonical
representation. If the value of the field is null, return 0 (or some other constant, but 0 is traditional).
To summarize above result, have a look at the sample class written. which takes into account all above rules/tips.
package com.bhoopendra.examples;
public class Test { private int num = 0; private String str = null; private long longField= 7L; private float salary = 200.56f; private boolean isManager = false; private Object myObj = new Object(); private short teamsize = 7; private char sex = 'M'; private double ppfMoney = 30000000000d; Test(String str, int num) { this.str = str; this.num = num; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (isManager ? 1231 : 1237); result = prime * result + (int) (longField ^ (longField >>> 32)); result = prime * result + ((myObj == null) ? 0 : myObj.hashCode()); result = prime * result + num; long temp; temp = Double.doubleToLongBits(ppfMoney); result = prime * result + (int) (temp ^ (temp >>> 32)); result = prime * result + Float.floatToIntBits(salary); result = prime * result + sex; result = prime * result + ((str == null) ? 0 : str.hashCode()); result = prime * result + teamsize; return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Test other = (Test) obj; if (isManager != other.isManager) return false; if (longField != other.longField) return false; if (myObj == null) { if (other.myObj != null) return false; } else if (!myObj.equals(other.myObj)) return false; if (num != other.num) return false; if (Double.doubleToLongBits(ppfMoney) != Double.doubleToLongBits(other.ppfMoney)) return false; if (Float.floatToIntBits(salary) != Float.floatToIntBits(other.salary)) return false; if (sex != other.sex) return false; if (str == null) { if (other.str != null) return false; } else if (!str.equals(other.str)) return false; if (teamsize != other.teamsize) return false; return true; } } |