Java集合之HashSet
首先上图:
由图可知,HashSet是Collection和Set接口的实现类。Set的一个重要特性就是元素不可重复,且元素无序(存入顺序与取出顺序不一致),这与List接口是恰恰相反的。HashSet是根据对象的哈希值来确定元素在集合中的存储位置,因此具有良好的存取和查找性能,其底层其实是一个HashMap的实例,它允许使用null元素。HashSet不是线程安全的。Set集合和List集合存取元素的方式都一样,直接通过一个简单的样例来演示HashSet集合的用法:
package practice;
import java.util.HashSet;
import java.util.Iterator;
public class HashSetPractice {
public static void main(String args[]) {
HashSet hs = new HashSet<>();
hs.add("jack");//添加元素
hs.add("eve");
hs.add("rose");
hs.add("rose");
hs.add(null);//允许null元素
Iterator it = hs.iterator();//获取迭代器
while(it.hasNext())//遍历集合
System.out.print(it.next()+" ");
System.out.println();
}
}
运行结果:null eve rose jack
由结果可以看出取出元素的顺序与添加元素顺序并不一致,并且重复存入的字符串对象“rose”只添加了一次。那么HashSet是如何保证不出现重复元素的呢?是因为它在存入元素时做了很多工作。当调用add()方法存入元素时,1.调用当前要存入对象的hashcode()方法获得对象的哈希值,2.根据对象的哈希值计算出一个存储位置,3.若该位置上没有元素,则直接将元素存入。4.若该位置上有元素存在,则会调用equals()方法让当前要存入的元素和该位置上的元素进行比较,若返回false,则将该元素存入集合(说明元素不重复),若equals()方法返回true,则说明元素重复,丢弃该元素。
根据前面的分析不难看出,HashSet是通过hashCode和equals方法保证元素不重复的。因为String类已经重写了hashCode和equals方法,所以不需要再次重写。但如果向HashSet中存入自定义类对象,结果又会如何呢?看下面代码:
package practice;
import java.util.HashSet;
import java.util.Iterator;
public class HashSetPractice {
public static void main(String args[]) {
HashSet<Student> hs = new HashSet<>();//new一个HashSet,泛型指定为自定义的类Student
Student s1 = new Student(1, "Jack");
Student s2 = new Student(2, "Rose");
Student s3 = new Student(2, "Rose");
hs.add(s1);//添加三个Student对象到集合中
hs.add(s2);
hs.add(s3);
System.out.println(hs);//输出集合中的元素
}
}
class Student{
int id;
String name;
public Student(int id ,String name) {
this.id = id;
this.name = name;
}
public String toString() {//重写toString方法,方便集合输出
return id+":"+name;
}
}
运行结果:[2:Rose, 2:Rose, 1:Jack]
这时我们发现运行结果中出现了两个相同的学生信息“2:Rose”,按照HashSet的规则,这种应该会被视为重复元素,不能重复出现。那为何会出现这种情况呢?因为在定义Student类时,没有重写hashCode和equals方法。接下来对Student类进行改写,我们认为id相同就是同一个学生,改写后的代码如下:
package practice;
import java.util.HashSet;
import java.util.Iterator;
public class HashSetPractice {
public static void main(String args[]) {
HashSet<Student> hs = new HashSet<>();//new一个HashSet,泛型指定为自定义的类Student
Student s1 = new Student(1, "Jack");
Student s2 = new Student(2, "Rose");
Student s3 = new Student(2, "Rose");
hs.add(s1);//添加三个Student对象到集合中
hs.add(s2);
hs.add(s3);
System.out.println(hs);//输出集合中的元素
}
}
class Student{
int id;
String name;
public Student(int id ,String name) {
this.id = id;
this.name = name;
}
public String toString() {//重写toString方法,方便集合输出
return id+":"+name;
}
public int hashCode() {
/*
* 因为基本数据类型int没有hashCode方法,
* 所以将它转为Integer,在调用HashCode方法
*/
Integer t = id;
return t.hashCode();
}
public boolean equals(Object obj) {
if(obj == this)//若是同一个对象,直接返回true
return true;
if(!(obj instanceof Student))//若对象不是Student类型,返回false
return false;
Student stu = (Student)obj;//先将对象强制转换为Student
return stu.id == this.id;//返回两个对象id比较后的结果
}
}
运行结果:[1:Jack, 2:Rose]
发现结果正常了,因为重写了hashCode和equals方法,所以HashCode集合认为s2和s3两个对象是相同的,所以去掉了重复的对象。