浅显易懂说 hashCode() equals()
hashCode() equals() 的逻辑关系是很抽象的。
首先要弄明白两个原理:
1,为什么不同的对象,会在特殊情况下却有相同的hashCode。可以举个例子吗?
这个涉及数据结构的知识,目前我正在研究这个问题,以后探讨。
2,两个相同的对象,hashCode一定是相同的。
根据上面的推导,java已经规范出来:
1,equals()相等那么hashCode()一定相等, 其中equals表示业务意义上相等的两个对象。这个根据原理2推导出
比如:
2,hashCode()相等 那么equals可以相等,也可以不相等,这个就是根据原理1推导出来
结论说明:
在java语言里面的那些 HashSet 和HashMap集合就根据上面的两条规范来设计的。具有去除相同对象的功能。
HashMap添加集合的时候,就是先用对象的hashCode定位数组槽位,然后比较equals是否相等。意思就是:hashCode相等,
equals也有可能不等,所以还要比较一下equals。如果比较有相同的equals,就覆盖去重了,比较不同,就追加到链表后。
因为两个不同的对象在特殊情况下也会有相等的hashCode,就是最开始说的那个原理1。
最后我们写程序的时候,用到hashSet和hashMap的时候,就要遵守上面的规范。
示例过程:
Student stu = new Student(1, "AAA");
Student stu2 = new Student(1, "AAA");
stu 和 stu2,我们在编程的时候,业务意义上一定要作为相等对象处理。既然是相等的对象那么hashCode一定是相等的。
所以我们要重写 equals() 和 hashCode();
public class Student {
private int id;
private String name;
public Student(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public boolean equals(Object o) {
// 就是比较一下 成员变量id和name的值是否相同。
return id == student.id &&
Objects.equals(name, student.name);
}
@Override
public int hashCode() {
//也是根据成员变量 id 和 name的值去换算,如果 两个对象的 id 和 name都相同的话,会返回一样的hashCode。
return Objects.hash(id, name);
}
}
Student stu = new Student(1, "AAA");
Student stu2 = new Student(1, "AAA");
HashSet<Student> set = new HashSet<>();
set.add(stu);
set.add(stu2);
for (Student student : set) {
System.out.println(student);
}
打印结构只有一个对象,可以去重,是正确的。
如果我们违背了上面的规范,比如 写个 equals相等,hashCode不等的实例代码,如下:
下面的程序没有重写hashCode方法,就利用了父类Object的hashCode,那么Object的hashCode,就不管你业务上的 id值和name值了,
返回的hashCode就基本上是不相等的。
public class Student {
private int id;
private String name;
public Student(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public boolean equals(Object o) {
// 就是比较一下 成员变量id和name的值是否相同。
return id == student.id &&
Objects.equals(name, student.name);
}
}
Student stu = new Student(1, "AAA");
Student stu2 = new Student(1, "AAA");
HashSet<Student> set = new HashSet<>();
set.add(stu);
set.add(stu2);
for (Student student : set) {
System.out.println(student);
}
打印结果:两个对象,不可以去重,是有bug的。