HashSet集合保证元素唯一性源码分析
HashSet集合添加元素的过程:
HashSet集合存储元素:
- HashSet要确保元素的唯一性,需要重写hashCode()和equal()方法
代码演示
图:
1. 不重写hashCode()和equal()方法下
Student类
package CCom.Cnn;
public class Student {
private String name;
private int Age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
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) {
Age = age;
}
}
Demo类
package CCom.Cnn;
import java.util.HashSet;
/*
HashSet集合保证元素唯一性源码分析
HashSet集合添加元素的过程:
HashSet要确保元素的唯一性,需要重写hashCode()和equal()方法
*/
public class Demo {
public static void main(String[] args) {
//建立HashSet集合
HashSet<Student> s = new HashSet<>();
//往HashSet集合存元素
Student s1 = new Student("沸羊羊",21);
Student s2 = new Student("美羊羊",22);
Student s3 = new Student("美羊羊",22);
s.add(s1);
s.add(s2);
s.add(s3);
//查询给对象的哈希地址
System.out.println(s.hashCode());//-714941564
System.out.println(s1.hashCode());//460141958
System.out.println(s2.hashCode());//1163157884
//注意,这里不重写下判断s2和s3的!!!地址!!!!相等不
System.out.println(s2.equals(s3));//false
//使用foreach来循环遍历(Iterator也行),注意这里会遍历的元素会重复
for(Student x:s){
System.out.println(x.getName()+x.getAge());
}
}
}
输出的内容:
-714941564
460141958
1163157884
false
美羊羊22
美羊羊22
沸羊羊21
2. 重写hashCode()和equal()方法下
Student类注意里面重写方法了
package CCom.Cnn;
import java.util.Objects;
public class Student {
//默认继承Object类,所以是可以重写父类方法的
private String name;
private int Age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
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) {
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);
}
}
Demo类
package CCom.Cnn;
import java.util.HashSet;
/*
HashSet集合保证元素唯一性源码分析
HashSet集合添加元素的过程:
HashSet要确保元素的唯一性,需要重写hashCode()和equal()方法
*/
public class Demo {
public static void main(String[] args) {
//建立HashSet集合
HashSet<Student> s = new HashSet<>();
//往HashSet集合存元素
Student s1 = new Student("沸羊羊",21);
Student s2 = new Student("美羊羊",22);
Student s3 = new Student("美羊羊",22);
//通过调用hashCode()方法,来查出该对象的位置,相当于门牌号
System.out.println(s.hashCode());//0
System.out.println(s1.hashCode());//861532894
System.out.println(s2.hashCode());//1005185097
//判断s1和s2的哈希值相等不
System.out.println(s2.hashCode()==s1.hashCode());//false
//此时此刻,判断s2和s3的!!!!内容!!!!!相等不
System.out.println(s2.equals(s3));//true
//不需要添加s3的内容了
s.add(s1);
s.add(s2);
//s.add(s3);不需要存入
//使用foreach来循环遍历(Iterator也行),此时便利的元素不重复
for(Student x:s){
System.out.println(x.getName()+x.getAge());
}
}
}
输出的内容
0
861532894
1005185097
false
true
美羊羊22
沸羊羊21
HashSet源码分析
从源码角度分析HashSet实现原理
-
面试官:请问HashSet有哪些特点?
应聘者:HashSet实现自set接口,set集合中元素无序且不能重复; -
面试官:那么HashSet 如何保证元素不重复?
应聘者:
- 因为HashSet底层是基于HashMap实现的,当你new一个HashSet时候,实际上是new了一个map,执行add方法时,实际上调用map的put方法,value始终是PRESENT,所以根据HashMap的一个特性: 将一个key-value对放入HashMap中时,首先根据key的hashCode()返回值决定该Entry的存储位置,如果两个key的hash值相同,那么它们的存储位置相同。
- 如果这个两个key的equalus比较返回true。那么新添加的Entry的value会覆盖原来的Entry的value,key不会覆盖。因此,如果向HashSet中添加一个已经存在的元素,新添加的集合元素不会覆盖原来已有的集合元素;
- 源码分析
先来看一下无参的构造函数:
public HashSet() {
map = new HashMap<>();
}
很显然,当你new一个HashSet的时候,实际上是new了一个HashMap
再来看一下add方法:
private static final Object PRESENT = new Object();
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
定义一个虚拟的Object PRESENT是向map中插入key-value对应的value,因为HashSet中只需要用到key,而HashMap是key—value键值对;所以,向map中添加键值对时,键值对的值固定是PRESENT
Set接口通常表示一个集合,其中的元素不允许重复(通过hashcode和equals函数保证),常用实现类有HashSet和TreeSet,HashSet是通过Map中的HashMap实现的,而TreeSet是通过Map中的TreeMap实现的。另外,TreeSet还实现了SortedSet接口,因此是有序的集合(集合中的元素要实现Comparable接口,并覆写Compartor函数才行)。