先学习参考资料
Java hashCode() 和 equals()的若干问题解答
先要有一个概念,在Object类里,equals和==功能是等效的。
我们自己写的类,如果不重写equals,代码逻辑上可能达不到我们想要的效果(尤其是扯到了hash,比如用object作为hashmap的key)。
拿具有去重功能的HashSet举例。
Demo1——天选之子String
import java.util.HashSet;
public class test1 {
public static void main(String[] args) {
HashSet hs = new HashSet<String>();
String a1 = "通话";//"通话"和"重地",是已知的会产生哈希冲突的字符串
String a2 = "重地";
System.out.println(a1.hashCode());//1179395
System.out.println(a2.hashCode());//1179395
hs.add(a1);
hs.add(a2);
hs.add("b");
System.out.println(hs);//[通话, 重地, b]
}
}
打印结果:
1179395
1179395
[通话, 重地, b]
是我们想要的结果,虽然发生了哈希冲突,但还是靠equals力挽狂澜成功去重。
问:为什么HashSet存String时,就不用重写?
答:因为人家的都被重写过了,String人送外号java亲儿子。因为String经常要被用到,所以java对String进行了特殊的处理。比如,它明明是个引用数据类型,但是很多场合会给人一种基本数据类型的错觉。懂的都懂,不懂的可以查一查java对String的特殊支持。
Demo2——单纯的学生类
public class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
我们来测试下HashSet的add()方法
import java.util.HashSet;
public class test2 {
public static void main(String[] args) {
HashSet hs = new HashSet<String>();
Student s1 = new Student("张三",123);
Student s2 = new Student("张三",123);
System.out.println(s1.hashCode());//685325104
System.out.println(s2.hashCode());//460141958
hs.add(s1);
hs.add(s2);
System.out.println(hs);//[Student{name='张三', age=123}, Student{name='张三', age=123}]
}
}
打印结果:
685325104
460141958
[Student{name='张三', age=123}, Student{name='张三', age=123}]
很明显,拉胯了。没有实现去重。
我们测试三次,看看如何才能不拉胯。
Demo3.1——单独重写学生类的HashCode()方法
建议使用快捷键重写
@Override
public int hashCode() {
return Objects.hash(name, age);
}
打印结果:
24022643
24022643
[Student{name='张三', age=123}, Student{name='张三', age=123}]
不错不错,虽然拉胯了,但是有进步。
已经知道根据name和age去计算hashCode了。
此时,我们再重写equals,HashSet的add去重功能就实现了。
但是,既然equals直接能判断内容,那我们直接重写equals就好了,为什么要重写hashCode呢?
Demo3.2——单独重写学生类的equals()方法
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age &&
Objects.equals(name, student.name);
}
打印结果:
685325104
460141958
[Student{name='张三', age=123}, Student{name='张三', age=123}]
太天真了,这hashCode都不一样,早就被判断为不同了,哪还有equals上场的机会。
Demo3.3——同时重写学生类的HashCode()、equals()方法
当对象相同,并且没有哈希碰撞时:
24022643
24022643
[Student{name='张三', age=123}]
当对象不同,但是发生哈希碰撞时:
36562329
36562329
[Student{name='重地', age=123}, Student{name='通话', age=123}]
当对象不同,并且没有哈希碰撞时:
24022643
26104975
[Student{name='李四', age=123}, Student{name='张三', age=123}]