Set接口介绍
Set接口继承自Collection,Set接口中没有新增方法,方法和Collection保持完全一致。我们在前面通过List学习的方法,在Set中用法相同。
【特点】
无序、不可重复。
-
无序指Set中的元素没有索引,只能遍历查找;
-
不可重复指不允许加入重复的元素,如果要加入的新元素和Set中某个元素通过equals()方法对比为true,则不予加入
Set常用的实现类有:【HashSet】、【TreeSet】等,我们一般使用HashSet
HashSet容器类
-
没有重复元素的集合,不保证元素顺序
-
HashSet允许有nul元素。HashSet是采用哈希算法实现,底层实际是HashMap实现的(HashSet本质就是一个简化版的HashMap),查询效率和增删效率都比较高
-
Hash算法也称之为散列算法:通过对哈希值进行模运算分散存放元素
【没有厕所的图书馆 张三 放身份证的带有一排格子的盒子】
HashSet的使用
for-each循环的优点:简洁,不同循环中可用相同名称的元素来进行
//实例化HashSet
Set<String> set = new HashSet<>();
//添加元素
set.add("a");
set.add("b1");
set.add("c2");
set.add("");
set.add("d");
set.add("a");
//获取元素,Set容器中没有索引,没有get(int index)方法,所以使用foreach循环;
允许有null元素,但不允许有重复元素
for (String str:set){
System.out.println(str);
}
//删除元素
boolean flag = set.remove("c2");
System.out.println(flag);
for (String str:set){
System.out.println(str);
}
//查询元素个数
int size = set.size();
System.out.println(size);
HashSet存储特征分析
- HashSet是一个不保证元素顺序且没有重复元素的集合,是线程不安全的
- HashSet允许有null元素
【无序】
在HashSet 的底层是使用HashMap存储元素的。HashMap底层使用的是数组与链表实现元素的存储。元素在数组中存放时,并不是有序
存放的也不是随机存放的,而是对元素的哈希值进行运算决定元素在数组中的位置
【不重复】
- 当两个元素的哈希值进行模运算后得到相同的结果,则它们在数组中的位置相同,此时会调用元素的equals()方法判断两个元素是否相同,若相同则不放入后者;若不同,按照先后顺序一个个单向链接
- 默认模运算以16进行,容器默认格子有16个
- 元素放在节点中,节点放在数组中,节点单链连接
- 对比顺序:哈希值—哈希值模运算的结果—元素本身
通过HashSet存储自定义对象!!!
//实例化HashSet
Set<Users> set1 = new HashSet<>();
Users u1 = new Users("cyf",26);
Users u2 = new Users("cyf",26);
set1.add(u1);
set1.add(u2);
System.out.println(u1.hashCode());//460141958
System.out.println(u2.hashCode());//1163157884
/**
* 为什么会有”看似出现重复元素“的现象?
* 是因为此处放入的元素是u1、u2(的地址),放入时根据u1、u2的hashcode判别
* 并不是放入u1、u2所包含的信息
*
* 如果通过重写hashcode()方法去比较u1、u2所包含信息本身的哈希值,肯定会判断相同,则不会放入u2!!!
*
* 所以可以通过重写hashcode()方法达到是否允许放入重复内容的目的
*/
for (Users users:set1){
System.out.println(users);
}
HashSet底层源码分析/P210
TreeSet容器类
-
TreeSet是一个支持排序的容器。底层实际是用TreeMap 实现的,内部维持了一个简化版的TreeMap,通过 key来存储Set的元素。
-
TreeSet内部需要对存储的元素进行排序,【我们需要给定排序规则】
-
排序规则实现方式:
通过元素自身实现比较规则
通过比较器指定比较规则
TreeSet的使用
public static void main(String[] args) {
//实例化TreeSet
Set<String> set = new TreeSet<>();
//添加元素,如无排序规则,需要先给定排序规则
set.add("c");
set.add("a");
set.add("d");
set.add("b");
set.add("a");
//获取元素
for (String str:set){
System.out.println(str);
}
}
通过元素自身实现排序规则
//实例化TreeSet
Set<String> set = new TreeSet<>();
//添加元素,如无排序规则,需要先给定排序规则
set.add("c");
set.add("a");
set.add("d");
set.add("b");
set.add("a");
//获取元素
for (String str:set){
System.out.println(str);
}
System.out.println("---------------------------");
Set<Users> set1 = new TreeSet<>();
Users u1 = new Users("wd",6);
Users u2 = new Users("whh",18);
Users u3 = new Users("awa",18);
set1.add(u1);
set1.add(u2);
set1.add(u3);
/**
* users cannot be cast to java.lang.Comparable
* 无法比较,无法排序
*
* 通过给Users类实现Comparable类,重写comparableTo方法,给定排序规则
* 先按年龄排,年龄相同的再按名字排
*/
for (Users users:set1){
System.out.println(users);
}
通过比较器指定比较规则
- 通过比较器定义比较规则时,需单独创建一个比较器,比较器需要实现Comparator接口中的compare方法来定义比较规则。在【实
例化TreeSet 】时将比较器对象交给TreeSet来完成元素的排序处理。此时元素自身就不需要实现比较规则了。
- 在main方法内,不能再定义私有的成员变量
为什么我觉得设置只比较年龄,它自动比较了姓名???
public class StudentComparator implements Comparator<Student> {
//定义比较规则
@Override
public int compare(Student o1, Student o2) {
if (o1.getAge() > o2.getAge()){
return 1;
}
if (o1.getAge() == o2.getAge()){
return o1.getName().compareTo(o2.getName());
}
return -1;
}
}
//在实例化TreeSet时将传递外部比较器,不依赖元素而独立存在
Set<Student> set2 = new TreeSet<>(new StudentComparator());
Student s1 = new Student("wbc",18);
Student s2 = new Student("qaq",22);
Student s3 = new Student("dwg",22);
Student s4 = new Student("wbc",20);
set2.add(s1);
set2.add(s2);
set2.add(s3);
set2.add(s4);
for (Student s:set2){
System.out.println(s);
}
单例集合使用案例
使用List类型容器实现
package com.cyf;
/**
* 需求:
* 产生1-10之间的随机数([1,10]闭区间),将不重复的10个随机数放到容器中
* 要求使用List类型容器实现
*/
import java.util.ArrayList;
import java.util.List;
public class ListDemo {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
while(true){
//产生足够多的随机数
int num = (int)(Math.random()*10+1);
//判断当前元素在容器中是否存在,如果不存在就添加,存在就重新随机
if (!list.contains(num)){
list.add(num);
}
//结束循环,直到存够10个数即停止
if (list.size() == 10){
break;
}
}
for (Integer i:list){
System.out.println(i);
}
}
}
使用Set类型容器实现
package com.cyf;
/**
* 需求:
* 产生1-10之间的随机数([1,10]闭区间),将不重复的10个随机数放到容器中
* 要求使用Set类型容器实现
*/
import java.util.HashSet;
import java.util.Set;
public class SetDemo {
public static void main(String[] args) {
Set<Integer> set = new HashSet<>();
while(true){
int num = (int)(Math.random()*10+1);
//将元素添加到容器中,Set容器不允许重复,所以不需判断
set.add(num);
//结束循环
if (set.size() == 10){
break;
}
}
/*
假排序现象!!!
HashSet不支持排序,TreeSet支持排序
为什么输出结果是自动排好序的?
整型的hashcode就是返回了value,按hashCode模运算的结果放入元素,并不是经过排序,碰巧
public static int hashCode(int value){
return value;
}
hashCode(value)%16
*/
for (Integer i: set){
System.out.println(i);
}
}
}