首先展示一下父类和子类的代码
父类是Student类,子类是Children类
import java.util.Objects;
public class Student {
private String name;
private int age;
public Student() {
}
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 +
'}';
}
@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);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
public class Children extends Student {
public Children() {
}
public Children(String name, int age) {
super(name, age);
}
}
然后是将Student类和Children类的对象放入Set集合中,即使两个对象中的属性值完全一样,Set集合也不会对其去重
package test.GenericDemo;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class SetDemo {
public static void main(String[] args) {
Set set = new HashSet();
Student s = new Student("zhang",12);
Children c = new Children("zhang",12);
set.add(s);
set.add(c);
Iterator it = set.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
}
结果为
测试后发现,即使Student类里面已经重写了equals方法,也没有去重
然后再试试看,写一个一样的Student类的对象
package test.GenericDemo;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class SetDemo {
public static void main(String[] args) {
Set set = new HashSet();
Student s = new Student("zhang",12);
Student s1 = new Student("zhang",12);
Children c = new Children("zhang",12);
set.add(s);
set.add(s1);
set.add(c);
Iterator it = set.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
}
结果和上面的一样
说明equals方法正常的运作了,但是判断父类对象和子类对象的时候出现了问题
在解决这个问题之前,需要先解释一下,Set集合去重的原理
具体的源码就不放出来了,简单来说就是首先计算传进来的元素的哈希值,如果传进来的元素为null,则返回哈希值为0,如果不是null,则计算该元素的哈希值。
而HashSet集合完成去重操作底层调用的是HashMap中的put()方法,借助了哈希表
根据元素之前计算出来的哈希值,再进行相关运算,得到元素在哈希表中的存储位置
如果该元素的位置null,表示该位置没有元素,可以存储,就创建新的结点,存储元素
如果该元素的位置不为null,则要进行比较
存入的元素和以前该位置的元素的哈希值进行比较
—— 如果哈希值不同,继续向下执行,把元素添加集合中
—— 如果哈希值一样,会调用对象的equals()方法进行比较
————如果比较equals()方法返回的是false,会继续向下执行,然后将元素插入到集合中
————如果比较equals()方法返回的是true,说明元素的哈希值和内容都一样,表示元素重复了
所以知道了原理后,再回过头来,去查看父类对象和子类对象的哈希值
package test.GenericDemo;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class SetDemo {
public static void main(String[] args) {
Set set = new HashSet();
Student s = new Student("zhang",12);
Student s1 = new Student("zhang",12);
Children c = new Children("zhang",12);
//查看对象的哈希值
System.out.println(s.hashCode());
System.out.println(s1.hashCode());
System.out.println(c.hashCode());
//比较对象之间的值
System.out.println(s.equals(s1));
System.out.println(s.equals(c));
}
}
这里发现,这些对象的哈希值相同,但是equals方法判断出父类对象和子类对象不同
因此才导致,子类对象被加入进了Set集合当中
那么现在,来看看Student类的equals方法
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);
}
可以看到,这一条判断语句if (o == null || getClass() != o.getClass()) return false;
再回过头去,看一看两个对象调用getClass()后的结果
package test.GenericDemo;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class SetDemo {
public static void main(String[] args) {
Set set = new HashSet();
Student s = new Student("zhang",12);
Student s1 = new Student("zhang",12);
Children c = new Children("zhang",12);
System.out.println(s.getClass());
System.out.println(c.getClass());
}
}
结果为
好了,到此终于知道为什么equals方法,会认为明明属性值都相同的父类对象和子类对象是不一样的了
那么,最后就是修改措施,将equals方法改为
public boolean equals(Object o) {
if (this == o) return true;
if(o instanceof Student){
Student student = (Student) o;
return age == student.age &&
Objects.equals(name, student.name);
}return false;
}
然后再运行一遍原先的遍历Set集合的语句
结果只剩下这一条语句
成功做到了,当父类对象与子类对象相同时,如果在Set集合中去重