什么是Set接口?
Set接口继承了Collection接口,Set不允许存储重复的元素,并且Set接口是无序的,没有索引,也就没有带索引的方法。
1、HashSet集合
HashSet集合的特点:
① 不允许存储重复的元素
② 没有索引,没有带索引的方法,也不能使用for循环遍历
③ 是一个无序集合,存储元素和取出元素的顺序可能不一致
④ 底层是一个哈希表结构
哈希值
哈希值是一个十进制的整数,由系统随机给出(就是对象的地址值,是一个逻辑地址,是模拟出来得到的地址,不是数据实际存储的物理地址),在Object类中的hashCode方法,可以获取对象的哈希值。int hashCode()
方法的源码是public native int hashCode()
其中native代表该方法调用的是本地操作系统的方法
对象的地址值是对象的hashCode的值的十六进制形式,我们需要注意这个hashCode是逻辑地址,可以覆盖重写,虽然再打印输出对象的地址能相等,但是实际的地址是不相等的,举例说明
public static void main(String[] args) {
Person p1 = new Person();
int h1 = p1.hashCode();
Person p2 = new Person();
int h2 = p2.hashCode();
System.out.println(h1);//得到对象p1的逻辑内存地址为1,因为覆盖重写了hashCode方法
System.out.println(h2);//得到的对象p2的逻辑内存地址为1
System.out.println(p1==p2);//得到的结果为false,因为实际的物理内存地址并不相等
}
public class Person(){
// 为了方便,只写出覆盖重写的方法
@override
public int hashCode() {
return 1;
}
}
String类的哈希值
String类重写了Object类的hashCode方法
public static void main(String[] args) {
String s1 = new String("abc");
String s2 = new String("abc");
System.out.println(s1);//String类重写了hashCode方法,s1和s2的逻辑地址相同
System.out.println(s2);
System.out.println("重地".hashCode());//显示的逻辑地址为117935
System.out.println("通话".hashCode());//显示的逻辑地址为117935
"重地"和"通话"的逻辑内存地址相同
哈希表
jdk1.8版本之前哈希表的结构是:数组+链表
jdk1.8版本之后哈希表的结构是:数组+红黑树(为了提高查询速度)
哈希表的特点就是查询速度快
2、Set集合不允许存储重复元素的原理
以HashSet集合为例
public static void main(String[] args) {
// 创建了HashSet集合对象
HashSet<String> set = new HashSet<>();
String s1 = new String("abc");
String s2 = new String("abc");
set.add(s1);
set.add(s2);
set.add("重地");
set.add("通话");
System.out.println(Set);//打印输出的结果是[重地, 通话, abc]
}
由上例子可以看出,即使我们存了重复的元素,但是我们打印输出的时候也不会有重复的元素
画图说明
文字说明:
当我们new HashSet<>();的时候再堆内存中创建了一个哈希表,Set集合在调用add方法的时候,add方法会调用元素的hashCode()方法和equals()方法进行判断存入哈希表中的元素是否重复
set.add(s1)
add方法会调用s1的hashCode方法,用于计算字符串"abc"的哈希值,得出"abc"的哈希值为96354,在集合中找有没有96354这个哈希值的元素,发现没有就会把s1存储到集合中
set.add(s2)
add方法会调用s2的hashCode方法,用于计算字符串"abc"的哈希值,得出"abc"的哈希值为96354,在集合中发现存在哈希冲突(集合中还有别的数据和s2的哈希值相同),紧接着s2会调用equals方法用来和s2哈希值相同的数据进行比较,因为String类重写了equals,能够判断s1和s2的内容是否相同,经过判断的到true,得到两个元素是相同的,那么就不会把s2存入哈希表
set.add(“重地”)
add方法会调用"重地"的hashCode方法,用于计算字符串"重地"的哈希值,得出"重地"的哈希值为1179395,在集合中找有没有1179395这个哈希值的元素,发现没有就会把"重地"存储到集合中
set.add(“通话”)
add方法会调用"通话"的hashCode方法,用于计算字符串"通话"的哈希值,得出"通话"的哈希值为1179395,在集合中发现存在哈希冲突(集合中还有别的数据和"通话"的哈希值相同),紧接着"通话"会调用equals方法用来和"通话"哈希值相同的数据进行比较,因为String类重写了equals,能够判断"通话"和"重地"的内容是否相同,经过判断的到false,得到两个元素是不相同的,那么就会把"通话"存入哈希表
前提是存储的元素必须重写hashCode方法和equals方法用于判断哈希值和内容是否相同
3、HashSet集合存储自定义类型的元素
如果想使用HashSet集合存储自定义类型的元素,那么要保证保存的元素是唯一的,所以我们必须重写hashCode方法和equals方法
LinkedHashSet集合
LinkedHashSet集合的底层是一个哈希表+链表,多了一条链表哟哦关于记录元素的存储顺序,保证元素的有序性,该集合存储的元素不能重复但是是有序的
举例说明
public static void main(String[] args) {
HashSet<String> set = new HashSet<>();
set.add("www");
set.add("abc");
set.add("abc");
set.add("kskd");
System.out.println(set);// 得到的结果是[abc,www,kskd]是一个无序的不重复的集合
LinkedHashSet<String> linked = new LinkedHashSet<>();
linked.add("www");
linked.add("abc");
linked.add("abc");
linked.add("kskd");
System.out.println(linked);// 得到的结果是[www,abc,kskd]是一个有序的不重复的集合
}