声明在哪?
equals() 和 hashCode() 声明在所有类的父类Object类中
public class Object {
public native int hashCode();
public boolean equals(Object obj) {
return (this == obj);
}
}
解释:
- equals 方法
- 当x和y引用的是同一个对象时,此方法返回true
- hashCode 方法
- navite 代表这是一个本地方法,作用:用来返回对象的哈希值(一个整数)。在Java程序执行期间,对一个对象调用此方法必须返回同样的哈希值
在源码中,这两个方法上有注释,大概是这个意思:
1. 如果两个对象调用 equals() 返回true,那么调用hashCode() 也必须得到相同的整数结果
2. 每当重写 equals() 时,hashCode() 也应该重写。和上一条要求相对应
hashCode() 的作用:是用来获取哈希值,而盖哈希值的作用是能根据此哈希值得到此对象在哈希表中的索引位置。
哈希表的典型代表是 HashMap
我们使用 HashSet 来演示,HashSet 的底层也是拿 HashMap 实现的
public HashSet() {
map = new HashMap<>();
}
为什么重写了 equals() 还要重写 hashCode()
来看下面这段代码:
public class EmpDTO {
//主键
private Long id;
//原密码
private String rawPassword;
//新密码
private String password;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
EmpDTO empDTO = (EmpDTO) o;
if (id != null ? !id.equals(empDTO.id) : empDTO.id != null) return false;
if (rawPassword != null ? !rawPassword.equals(empDTO.rawPassword) : empDTO.rawPassword != null) return false;
return password != null ? password.equals(empDTO.password) : empDTO.password == null;
}
public static void main(String[] args) {
Set<EmpDTO> set = new HashSet<>();
EmpDTO e1 = new EmpDTO();
e1.setId(1L);
EmpDTO e2 = new EmpDTO();
e2.setId(1L);
set.add(e1);
System.out.println("e1.equals(e2) = " + e1.equals(e2));
System.out.println("e1.hashCode() = " + e1.hashCode());
System.out.println("e2.hashCode() = " + e2.hashCode());
for (EmpDTO empDTO : set) {
System.out.println(empDTO);
}
}
}
在阅读下面的逻辑前,我们应该先了解一下 HashSet 的一点原理:
Set 的特点:无序性,不可重复性
我们在往 HashSet 中放数据时,假设放的是 `元素a`,首先调用 `元素a` 所在类的 hashCode() 方法,来得到哈希值,然后在根据某种算法,来得到在集合中存放的位置(即为:索引位置)。判断此位置上是否有元素:
- 如果没有,则添加成功 ----> 情况一
- 如果有 `元素b`或以链表形式存在的多个元素,先比较 `元素a` 和 `元素b` 的hash值:
- 如果不相同:则 `元素a` 添加成功,以链表方式存储(原来的元素在数组中,指向 `元素a`) ---> 情况二
- 如果相同:调用 `元素a` 所在类的 equals() 方法
- 如果返回 true,则添加成功 ---> 情况三
- 如果返回 false,则添加失败
好了,我们已经简单了解了 HashSet 的原理了,现在让我们往下走
我们new了一个 set 集合,往里面放了两个 `EmpDTO`类的对象。而我们重写了 `EmpDTO` 的类的 equals 方法,我们new了两个 `EmpDTO` 的两个对象,此时使用 equals 方法进行比较,结果为 `true`。此时遍历 set 集合中,有两个 `EmpDTO` 类的对象:
此时的 equals 方法结果为true,但是调用 hashCode 得到的值却不同
输出结果:
e1.equals(e2) = true
e1.hashCode() = 610998173
e2.hashCode() = 2047329716
EmpDTO(id=1, rawPassword=null, password=null)
EmpDTO(id=1, rawPassword=null, password=null)
原因:因为两个对象的 hash 值不相同,所以在 HastSet 中存储的位置也不同,所以能添加成功
现在我们来重写 hashCode() 方法,
在 idea 中直接 Alt+Insert 选择 equals() and hashCode(),一直点击 `next` 即可
hashCode():
@Override
public int hashCode() {
int result = id != null ? id.hashCode() : 0;
result = 31 * result + (rawPassword != null ? rawPassword.hashCode() : 0);
result = 31 * result + (password != null ? password.hashCode() : 0);
return result;
}
此时再执行,main方法中代码逻辑后,得出的结果为:
e1.equals(e2) = true
e1.hashCode() = 961
e2.hashCode() = 961
EmpDTO(id=1, rawPassword=null, password=null)
此时 set 中只有一个元素了。equals 方法得到的结果为true,hashCode 方法得到的哈希值也一致
每当重写 equals 方法时,hashCode 方法也需要重写。是为了保证:如果两个对象调用 equals() 方法返回的结果为 true ,那么两个对象调用 hashCode() 方法返回的哈希值也必然相同!
好了,晚安。