HashSet是基于HashMap实现的,它的去重原理就是利用HashMap的key不能重复的特性。
在HashSet中,元素被存放在HashMap的key中,value则是一个常量对象,所以HashSet能够保证元素的不重复。
当我们向 HashSet 中添加元素时,HashSet 会先调用元素的 hashCode() 方法得到元素的哈希值,然后根据哈希值计算出元素在 HashMap 中的存储位置。如果该位置上已经有元素了,HashSet 会调用该元素的 equals() 方法与新元素进行比较,如果 equals() 方法返回 true,HashSet 就认为这两个元素是相同的,不会将新元素添加进去。如果 equals() 方法返回false,HashSet 就会将新元素添加进去。
但是会存在一些特殊的情况:两个元素的hashCode()相同,但是他们的值不同。例如:
public static void main(String[] args) {
String a = "通话";
String b = "重地";
System.out.println(a.hashCode());
System.out.println(b.hashCode());
System.out.println(a==b);
}
运行结果为:
我们可以认为在HashSet中通过判断两个元素的hascode() 和 equals()得到的结果是否相同来判断他们是否是同一个值,如果相同,则认为是同一个。
当HashSet的泛型是基本类型时,官方已经将基本类型的hascode() 和 equals()重写了,但是在我们自定义的类中却没有,因此,如果我们想要在HashSet的泛型为我们自定义的类型中正确去重,就需要重写元素的hashCode()和equals()方法,使它们能够正确地比较元素的内容。
判断重复的步骤:
①: 通过hashcode判断hashcode值是否相同
②:如果hashcode的值相同在判断equals是否相同,均相同认为是同一个元素
举个例子:定义一个student类
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
}
设置a和b两个对象,并将其添加进HashSet集合set中
public static void main(String[] args) {
Student a = new Student("张飞", 25);
Student b = new Student("张飞", 25);
HashSet<Student> set = new HashSet<Student>();
System.out.println(set.add(a));
System.out.println(set.add(b));
System.out.println(a.hashCode());
System.out.println(b.hashCode());
System.out.println(set);
}
运行结果为:
可以看出添加的两个元素的key值相同,value值也相同,按照HashSet的定义因该是不能同时存在于一个集合中的,但是运行后得出的hashCode()值不同,因此都可以添加进set集合中,这个时候,就需要在Student类中重写hascode() 和 equals()方法了。
重写方法后的Student类:
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int hashCode() {
// TODO Auto-generated method stub
return Objects.hash(this.name,this.age);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student student = (Student)obj;
return Objects.equals(name , student.name);
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
}
再次运行前面添加的a和b元素,得到的结果为:
可以看到再次运行后,第二次在添加相同的元素就添加不进去了,也就达到了我们想要的结果