比较同一个类的两个实例的值是否相等,是开发中比较常用的功能,那么重写equals方法,就是我们必须做的,但是为什么重写了equals方法必须重写hashcode方法呢?
1 为什么要重写equals方法?
这个问题很容易理解,所有类都是直接或间接的继承自Object类,在Object类中,equals方法是这样定义的
public boolean equals(Object obj) {
return (this == obj);
}
直接比较的是两个对象的内存地址,所以如果我们想比较两个对象的值是否相等,必须重写equals方法。
2 为什么String 可以直接使用equals 方法,而不需要我们重写equals方法?
在String类中已经对equals方法和hashcode方法进行了重写,不需要我们再次重写
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
3 重写了equals方法,而不重写 hashcode 方法会怎么样?
1.不重写equals 方法
public class Student {
private String name;
private int age;
private String interest;
}
public class App {
public static void main(String[] args) {
Student stu1 = new Student();
Student stu2 = new Student();
System.out.println("stu1是否和stu2相等:"+stu1.equals(stu2));
}
}
测试结果:
这个结果和我们预期的一样
2 只是重写equals方法
public class Student {
private String name;
private int age;
private String interest;
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (age != other.age)
return false;
if (interest == null) {
if (other.interest != null)
return false;
} else if (!interest.equals(other.interest))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
public class App {
public static void main(String[] args) {
Student stu1 = new Student();
stu1.setName("张三");
Student stu2 = new Student();
stu2.setName("张三");
System.out.println("重写equals方法后stu1是否和stu2相等:"+stu1.equals(stu2));
Student stu3 = new Student();
stu3.setName("张三");
Student stu4 = new Student();
stu4.setName("李四");
System.out.println("重写equals方法后stu3是否和stu4相等:"+stu3.equals(stu4));
}
}
测试结果:
跟我们预期的一样,此时已经可以用equals 判断两个对象的值是否相等,那我们是否还有必要重写hashcode方法呢?
继续看如下的测试:
public class App {
public static void main(String[] args) {
Student stu1 = new Student();
Student stu2 = new Student();
Student stu3 = new Student();
Student stu4 = new Student();
stu1.setName("张三");
stu2.setName("张三");
stu3.setName("张三");
stu4.setName("李四");
Set<Student> set = new HashSet<>();
set.add(stu1);
set.add(stu2);
set.add(stu3);
set.add(stu4);
System.out.println("重写equals方法后:"+set.size());
}
}
测试结果如下:
set集合的大小为 4
3 equals方法和hashcode 方法都重写
public class Student {
private String name;
private int age;
private String interest;
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((interest == null) ? 0 : interest.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (age != other.age)
return false;
if (interest == null) {
if (other.interest != null)
return false;
} else if (!interest.equals(other.interest))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
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;
}
public String getInterest() {
return interest;
}
public void setInterest(String interest) {
this.interest = interest;
}
}
public class App {
public static void main(String[] args) {
Student stu1 = new Student();
Student stu2 = new Student();
Student stu3 = new Student();
Student stu4 = new Student();
stu1.setName("张三");
stu2.setName("张三");
stu3.setName("张三");
stu4.setName("李四");
Set<Student> set = new HashSet<>();
set.add(stu1);
set.add(stu2);
set.add(stu3);
set.add(stu4);
System.out.println("重写equals和hashcode方法后:"+set.size());
System.out.println(stu1.hashCode());
System.out.println(stu2.hashCode());
System.out.println(stu3.hashCode());
System.out.println(stu4.hashCode());
}
}
测试结果如下:
我们创建了4个实例,扔进了set集合中,但是set集合的大小的是2,是不是有点不可思议?
为什么会这样呢?因为往set集合中添加对象时,set首先会获取该对象的hashcode值,然后在判断是否已经保存有该hashcode值得对象,没有重写hashcode方法之前,我们获取的对象的hashcode值时,取到的是其内存地址,两个对象的内存地址,肯定不同,但是我们重写了hashcode值之后,我们所获取的hashcode值不再是内存地址,而是根据对象的属性计算出来的一个int值,如果同一类的两个对象的每个属性的值都是相等的,那么计算出来的hashcode值一定也是相等,这也就解释了为什么重写hashcode方法之后,我们明明往set 中扔了4个对象,但是set集合大小为2 的原因。
4 只是重写了equals方法 不重写hashcode 方法可以吗?
个人觉得是可以的,但是最好还是重写一下hashcode