由于equal()和hashcode()是object中的方法,所以每个类中都有这两个方法,但在实际应用中往往需要重写这两个方法。
重写equal()和hashcode()方法
假设场景:java中的Set集合的特性之一是会去除相同的元素。假设在Set集合中存学生对象,同一个人只能在Set集合中存一次,判断一个学生是否是同一个人的依据为:姓名相同,年龄相同,即为同一人。
编写一个Student类:
public class Student {
private String name;
private Integer age;
public Student(String name, Integer age) {
this.name = name;
this.age = age;
}
//todo 添加get/set 方法
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
编写一个测试类:
public class SetTest {
/**
* 假设一场景:判断一个学生是否是同一个人的依据为:姓名相同,年龄相同,即为同一人
* @param args
*/
public static void main(String [] args){
Set set = new HashSet();
set.add(new Student("xiaoming",22));
set.add(new Student("liangliang",24));
set.add(new Student("xiaoming",22));
set.add(new Student("daming",23));
set.add(new Student("xiaoming",23));
for (Iterator it =set.iterator();it.hasNext();){
System.out.println(it.next());
}
}
}
测试结果:
显然,结果中出现两个xiaoming,年龄都为22,应该判断为一个人。出现这个结果是因为new出的每一个对象,哈希值是不同的, 想要实现预定的需求,必然要重写equal方法和hashcode方法。
在student类中重写这两个方法:
@Override
public int hashCode() {
return this.name.hashCode()+age * 31;
}
@Override
public boolean equals(Object obj) {
if(this == obj)
return true;
if(!(this instanceof Student)){
throw new ClassCastException("类型不一样");
}
Student stu = (Student) obj;
return this.age == stu.age && this.name == stu.name;
}
重写equal方法和hashcode方法有多种,可以根据自己的实际需求来指定合适的重写方法。
测试结果:
实现所需要的结果。
思考:为什么要重写equal和hashcode方法,这两个方法的调用机制是什么?
java中引入hashcode的方法主要就是为了查找方便,如果是一个数组去实现,想要找到其中一个值,就需要一个一个去比较,在数据量很大的情况下,性能会特别差,用hashcode方法可以快速找到存储的位置。那是否只重写hashcode()方法就可以呢?答案是否定的。只覆写hashcode 的方法,还是得不到预期的结果,因为同名同年龄得到的哈希值是一样的,当hash值一样时,就会去比较equal方法,此时调用的是object中的equal()方法,比较的是地址,都是新new出对象,地址不一样,所以equal返回为false,判断出两个学生不是同一人,所以要同时覆写equal方法。
这两个调用机制是什么呢,是先调用equal()方法,还是先调用hashcode()方法,接下来做个小实验,在这两个方法中分别打印出调用的对象:
@Override
public int hashCode() {
System.out.println(this.getName()+"调用hashcode方法:"+this.name.hashCode()+age * 31);
return this.name.hashCode()+age * 31;
// return age ;
}
@Override
public boolean equals(Object obj) {
if(this == obj)
return true;
if(!(this instanceof Student)){
throw new ClassCastException("类型不一样");
}
Student stu = (Student) obj;
System.out.println(this.getName()+"调用equal方法" +stu.getName());
//此equal()方法为String中的方法
return this.age == stu.age && this.name.equals( stu.name);
}
测试结果:
所以,在比较是否为同一个人时,首先会调用它的hashcode()方法,只有当hashcode()值一样是才回去调用equal()方法。第二个xiaoming是先调用hashcode()方法,发现和第一个xiaoming的哈希值是星等的,才再去比较equal()方法。这种机制会减少很多比较,提升效率。
如果两个对象的hashcode值不同,则必是两个不同的对象;但,两个不同的对象所产生的hashcode值是可能一样的,称为哈希碰撞。
最后欣赏下IDEA自动生成的equal()和hashcode()方法:
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return Objects.equals(name, student.name) &&
Objects.equals(age, student.age);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}