一、Set集合
1.背景
Set继承自Collection,和 List 一样属于单列集合。
2.Set的特点
Set具体实现类具有以下共同特点:
- 不重复:可以去除重复
- 无索引:没有带索引的方法(因此不能使用普通for循环遍历,也不能通过索引来获取元素)
不重复:①指添加重复元素只有第一个能成功 ②当强制转换为Set集合时,会自动去除重复元素
注意:Set并不是具体类,而是公共接口,所以创建实例时必须用其实现类HashSet、LinkedHashSet或TreeSet,后面总结它们的区别
二、HashSet集合
1.特点
- HashSet集合底层采用哈希表存储数据
- HashSet是无序的
注:此处的无序
2.浅谈哈希表
(1)哈希表的组成:
- JDK8之前的,底层使用
数组+链表
组成 - JDK8开始后,底层采用
数组+链表+红黑树
组成
(2)哈希表的实现细节
详见好文:HashMap解析 在此简述一下:哈希表的底层是一个称为哈希桶
的数组,在添加元素时,会采用哈希算法进行填充,此时涉及到数组长度以及填充因子等因素。
不同的哈希算法会导致散列度不同。
注:散列度
是指元素填充的分散程度,越分散,填充时越不容易发生冲突。
当填充发生冲突时,采用的是链地址法。链地址法
,简单来说,就是数组加链表的结合。在每个数组元素上都一个链表结构,当数据被Hash后,得到数组下标,把数据放在对应下标元素的链表上
在JDK8之后,引入了红黑树,即当一个位置上挂载的元素超过一定数量之后,就会采用红黑树结构,有效提高访问效率。
三、LinkedHashSet、TreeSet与HashSet的区别
1.LinkedHashSet与HashSet的区别
它们基本特点相同,即底层都是哈希表实现元素存储,但是前者是有序的。
因为:链表哈希集合在内部为其所有元素维护一个双链表。链表定义了在哈希表中插入元素的顺序。
2.LinkedHashSet的创建
链表哈希集合有两种创建方式。
(1)先初始化,再添加元素
代码如下:
//具有8个容量和0.75负载因子的LinkedHashSet
LinkedHashSet<Integer> numbers = new LinkedHashSet<>(8, 0.75);
此处,第一个参数是容量,第二个参数是负载因子。
- capacity - 该哈希集的容量为8。意味着,它可以存储8个元素。
- loadFactor- 此哈希集的负载因子为0.6。这意味着,只要我们的哈希表填充了60%,元素就会移动到新哈希表中,该哈希表的大小是原始哈希表的两倍。
也可以不写参数,即采用默认容量和负载因子。
- 链接哈希集的容量将为16
- 负载因子将为0.75
(2)导入其它集合元素
使用addAll()方法,可以将指定集合的所有元素插入链表的哈希集,但是此方法有一个问题,就是其元素顺序,是由导入集合的顺序决定的。
例如:
Collection<String> hashSet = new HashSet<>();
Collection<String> linkedHashSet = new LinkedHashSet<>();
hashSet.add("windx");
hashSet.add("xlk");
hashSet.add("Bill");
hashSet.add(null);
hashSet.add(null);
linkedHashSet.addAll(hashSet); //如果直接添加hasSet集合元素,则LinkedHashSet顺序和其相同
此时,linkedHashSet集合的顺序与HashSet集合顺序一致,结果如下
2.TreeSet与HashSet的区别
TreeSet集合底层是基于红黑树
的数据结构实现排序的,增删改查性能都较好。
注意:TreeSet集合是一定要排序的,可以将元素按照指定的规则进行排序。
3.关于TreeSet集合的排序
(1)默认排序规则
- 对于数值类型:Integer , Double,官方默认按照大小进行升序排序。
- 对于字符串类型:默认按照首字符的编号升序排序。
- 对于自定义类型如Student对象,TreeSet无法直接排序,但可自定义排序
(2)自定义排序规则
方法一:让自定义的类(如学生类)实现Comparable接口重写里面的compareTo方法来定制比较规则。
方法二:TreeSet集合有参数构造器,可以设置Comparator接口对应的比较器对象,来定制比较规则。
比较规则:
①第一个元素大于第二个元素—返回正整数
②第一个元素小于第二个元素—返回负整数
③第一个元素等于第二个元素—返回0