hashCode()和equals()定义在Object类中,这个类是所有java类的基类,所以所有的java类都继承这两个方法。
?
使用hashCode()和equals()
hashCode()方法被用来获取给定对象的唯一整数。这个整数被用来确定对象被存储在HashTable类似的结构中的位置。默认的,Object类的hashCode()方法返回这个对象存储的内存地址的编号。
重写默认的实现
如果你不重写这两个方法,将几乎不遇到任何问题,但是有的时候程序要求我们必须改变一些对象的默认实现。
来看看这个例子,让我们创建一个简单的类Employee
?
monospace !important; float: none !important; height: auto !important; font-size: 10pt !important; vertical-align: baseline !important; font-weight: normal !important; padding-top: 0px; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; border: #dddddd 1px solid;">01
class="keyword" style="background-image: none !important; text-align: left !important; padding-bottom: 0px; line-height: 1.1em !important; background-color: #f6f6f6; font-style: normal !important; margin: 0px 2px; padding-left: 5px; width: auto !important; padding-right: 5px; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; float: none !important; height: auto !important; color: #006699 !important; font-size: 10pt !important; vertical-align: baseline !important; font-weight: bold !important; padding-top: 0px; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; border: #dddddd 1px solid;">public?class?Employee
02
{
03
????private?Integer id;
04
????private?String firstname;
05
????private?String lastName;
06
????private?String department;
07
?
08
????public?Integer getId() {
09
????????return?id;
10
????}
11
????public?void?setId(Integer id) {
12
????????this.id = id;
13
????}
14
????public?String getFirstname() {
15
????????return?firstname;
16
????}
17
????public?void?setFirstname(String firstname) {
18
????????this.firstname = firstname;
19
????}
20
????public?String getLastName() {
21
????????return?lastName;
22
????}
23
????public?void?setLastName(String lastName) {
24
????????this.lastName = lastName;
25
????}
26
????public?String getDepartment() {
27
????????return?department;
28
????}
29
????public?void?setDepartment(String department) {
30
????????this.department = department;
31
????}
32
}
上面的Employee类只是有一些非常基础的属性和getter、setter.现在来考虑一个你需要比较两个employee的情形。
?
?
01
public?class?EqualsTest {
02
????public?static?void?main(String[] args) {
03
????????Employee e1 =?new?Employee();
04
????????Employee e2 =?new?Employee();
05
?
06
????????e1.setId(100);
07
????????e2.setId(100);
08
????????//Prints false in console
09
????????System.out.println(e1.equals(e2));
10
????}
11
}
毫无疑问,上面的程序将输出false,但是,事实上上面两个对象代表的是通过一个employee。真正的商业逻辑希望我们返回true。?
为了达到这个目的,我们需要重写equals方法。?
01
public?boolean?equals(Object o) {
02
????????if(o ==?null)
03
????????{
04
????????????return?false;
05
????????}
06
????????if?(o ==?this)
07
????????{
08
???????????return?true;
09
????????}
10
????????if?(getClass() != o.getClass())
11
????????{
12
????????????return?false;
13
????????}
14
????????Employee e = (Employee) o;
15
????????return?(this.getId() == e.getId());
16
}
在上面的类中添加这个方法,EauqlsTest将会输出true。?
So are we done?没有,让我们换一种测试方法来看看。?
01
import?java.util.HashSet;
02
import?java.util.Set;
03
?
04
public?class?EqualsTest
05
{
06
????public?static?void?main(String[] args)
07
????{
08
????????Employee e1 =?new?Employee();
09
????????Employee e2 =?new?Employee();
10
?
11
????????e1.setId(100);
12
????????e2.setId(100);
13
?
14
????????//Prints 'true'
15
????????System.out.println(e1.equals(e2));
16
?
17
????????Set<Employee> employees =?new?HashSet<Employee>();
18
????????employees.add(e1);
19
????????employees.add(e2);
20
????????//Prints two objects
21
????????System.out.println(employees);
22
????}
上面的程序输出的结果是两个。如果两个employee对象equals返回true,Set中应该只存储一个对象才对,问题在哪里呢??
我们忘掉了第二个重要的方法hashCode()。就像JDK的Javadoc中所说的一样,如果重写equals()方法必须要重写hashCode()方法。我们加上下面这个方法,程序将执行正确。
1
@Override
2
?public?int?hashCode()
3
?{
4
????final?int?PRIME =?31;
5
????int?result =?1;
6
????result = PRIME * result + getId();
7
????return?result;
8
?}
使用Apache Commons Lang包重写hashCode() 和equals()方法?
Apache Commons 包提供了两个非常优秀的类来生成hashCode()和equals()方法。看下面的程序。
?
view source print?01
import?org.apache.commons.lang3.builder.EqualsBuilder;
02
import?org.apache.commons.lang3.builder.HashCodeBuilder;
03
public?class?Employee
04
{
05
?private?Integer id;
06
?private?String firstname;
07
?private?String lastName;
08
?private?String department;
09
public?Integer getId() {
10
????return?id;
11
?}
12
?public?void?setId(Integer id) {
13
????this.id = id;
14
?}
15
?public?String getFirstname() {
16
????return?firstname;
17
?}
18
?public?void?setFirstname(String firstname) {
19
????this.firstname = firstname;
20
?}
21
?public?String getLastName() {
22
????return?lastName;
23
?}
24
?public?void?setLastName(String lastName) {
25
????this.lastName = lastName;
26
?}
27
?public?String getDepartment() {
28
????return?department;
29
?}
30
?public?void?setDepartment(String department) {
31
????this.department = department;
32
?}
33
@Override
34
?public?int?hashCode()
35
?{
36
????final?int?PRIME =?31;
37
????return?new?HashCodeBuilder(getId()%2==0?getId()+1:getId(), PRIME).
38
???????????toHashCode();
39
?}
40
@Override
41
?public?boolean?equals(Object o) {
42
????if?(o ==?null)
43
???????return?false;
44
????if?(o ==?this)
45
???????return?true;
46
????if?(o.getClass() != getClass())
47
???????return?false;
48
????Employee e = (Employee) o;
49
???????return?new?EqualsBuilder().
50
??????????????append(getId(), e.getId()).
51
??????????????isEquals();
52
????}
53
?}
如果你使用Eclipse或者其他的IDE,IDE也可能会提供生成良好的hashCode()方法和equals()方法。?

需要注意记住的事情
当使用ORM的时候特别要注意的
public int hashCode() {?
? ??int h = hash;?
? int len = count;?
? if (h == 0 && len > 0) {?
? int off = offset;?
? char val[] = value;?
? for (int i = 0; i < len; i++) {?
? ? ?h = 31*h + val[off++];?
? ?}?
? hash = h;?
? }?
? return h;?
}?
该函数是我看的函数接口源码,为什么要使用31这个数呢?
?
其实上面的实现也可以总结成数数里面下面这样的公式:
s[0]*31^(n-1) + s[1]*31^(n-2) + … + s[n-1]
A.31是一个素数,素数作用就是如果我用一个数字来乘以这个素数,那么最终的出来的结果只能被素数本身和被乘数还有1来整除!。(减少冲突)
B.31可以 由i*31== (i<<5)-1来表示,现在很多虚拟机里面都有做相关优化.(提高算法效率)
C.选择系数的时候要选择尽量大的系数。因为如果计算出来的hash地址越大,所谓的“冲突”就越少,查找起来效率也会提高。(减少冲突)
D.并且31只占用5bits,相乘造成数据溢出的概率较小。