Java基础-equals()和hashCode()
1.equals()和hashCode()干嘛的?
equals(Object obj)返回该对象是否与参数对象“相等”,Object类的equals方法实现如下,即基本类型是值比较,引用类型是内存地址比较。Object类中equals()方法实现如下:
public boolean equals(Object obj) {
return (this == obj);
}
hashCode()返回该对象的哈希码值,在哈希表中起作用,以提高集合的查找效率,例如HashSet,HashMap。
2.equals()和hashCode()有何联系?
JDK中对equals()方法和hashCode()方法之间的联系做了说明:
- 如果两个对象使用equals()方法判断为相等,则hashCode()方法也应该相等。
为何这样规定?
以HashMap为例,HashMap底层以数组和链表结构存储数据,如图所示:
114006_HzFL_2927759.png
HashMap的put()方法根据键对象的hashCode()方法计算其哈希码,从而确定该键值对在数组中的位置,再根据键对象的equals()判断链表中是否有相同对象,最后选择是否插入该对象,而get()方法也是先调用键对象的hashCode()确定数组位置再通过equals()方法逐一在链表中寻找对象。
由此可知,如果两个键对象hashCode()不相等,那么它们会被存储在“数组”的不同位置。规定“两个对象使用equals()方法判断为相等,则hashCode()方法也应该相等”可以避免在类似HashMap这样的集合中存储两份相同对象的可能。
3.equals()和hashCode()如何重写?
参考博文
《Effective Java》中提出了一种简单通用的hashCode算法
A、初始化一个整形变量,为此变量赋予一个非零的常数值,比如int result = 17;
B、选取equals方法中用于比较的所有域(之所以只选择equals()中使用的域,是为了保证上述原则),然后针对每个域的属性进行计算:
(1) 如果是boolean值,则计算f ? 1:0
(2) 如果是byte\char\short\int,则计算(int)f
(3) 如果是long值,则计算(int)(f ^ (f >>> 32))
(4) 如果是float值,则计算Float.floatToIntBits(f)
(5) 如果是double值,则计算Double.doubleToLongBits(f),然后返回的结果是long,再用规则(3)去处理long,得到int
(6) 如果是对象应用,如果equals方法中采取递归调用的比较方式,那么hashCode中同样采取递归调用hashCode的方式。否则需要为这个域计算一个范式,比如当这个域的值为null的时候,那么hashCode 值为0
(7) 如果是数组,那么需要为每个元素当做单独的域来处理。
C、最后,把每个域的散列码合并到对象的哈希码中。
public class Person {
private String name;
private int age;
public Person() {
super();
}
@Override
public boolean equals(Object another) {
if (this == another) {
return true;
}
if (another instanceof Person) {
Person anotherPerson = (Person) another;
if (this.getName().equals(anotherPerson.getName()) && this.getAge() == anotherPerson.getAge()) {
return true;
} else {
return false;
}
}
return false;
}
@Override
public int hashCode() {
int hash = 17;
hash = hash * 31 + getName().hashCode();
hash = hash * 31 + getAge();
return hash;
}