一、前言
1.为什么要使用Set集合?
1.1.去重
Set集合中的元素是不重复的,如果我们需要去除一个列表或元组中的重复元素,就可以转换为set类型来达到去重的目的。
1.2.提高查找速度
由于Set集合是通过哈希表实现的,所以查找元素的时间复杂度为O(1),相对于列表或元组来说,它的查找速度更快。
1.3.数学运算
集合论是计算机科学中的重要分支,Set集合可以用于实现集合论中的各种运算,如并集、交集、差集等。
2.使用场景
在公司内有可能要用别人的接口,而别人对于传过来的数据可能是有重复或者杂乱无章的,因为不是我们的数据库不能进行sql语句的编辑,达到自己想要的效果,那么就可以用到set集合,从而达到我们的去重复和排序的需求。
二、Set集合
1.Set特点
1.1.无序: 元素输入的顺序与插入的顺序不一致。
Set<Integer> set=new HashSet<Integer>();
set.add(2);
set.add(1);
set.add(5);
set.add(8);
System.out.println(set);
1.2.元素唯一: 不允许添加重复的元素。
Set<Integer> set=new HashSet<Integer>();
set.add(2);
set.add(1);
set.add(5);
set.add(8);
set.add(8);
System.out.println(set);
那这里如果添加重复元素是覆盖?还是过滤?怎么实现去除重复的?我们来做个实验
//我们知道Set存储字符串可以去重,那么存储对象呢?
HashSet<stu> s=new HashSet<stu>();
s.add(new stu(1, "xw"));
s.add(new stu(2, "wh"));
//是否包含
boolean contains = s.contains(new stu(1, "xw"));
System.out.println(contains);
// 这里打印结果是false,不包含。
}
}
这里发现同样的名字居然可以重复存储,那这不是违背了Set的特点吗?其实不然,因为对象是new出来的,在内存上开辟了一个新的内存地址,如果不重写equals方法和hashCode方法默认比较的就是内存地址;
⚠注意:如果只重写equals方法,默认还是比较内存地址,需要equals方法和hashCode方法一起重写,才能达到去除重的效果,当我hashCode一致时候,会进行equals比较。如果equals返回的是true,那说明一致,则不会添加,也就是过滤掉了。
2.Set集合遍历方式
foreach遍历
Set<String> set=new HashSet<String>();
set.add("xw");
set.add("wh");
set.add("xwaiwh");
for (String value : set) {
System.out.println(value);
}
迭代器
Set<String> set=new HashSet<String>();
set.add("xw");
set.add("wh");
set.add("xwaiwh");
Iterator<String> it = set.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
三、Set常见的实现类
1.HashSet
HashSet特点
- 无序性:HashSet中的元素是无序的,即元素在HashSet中的位置和插入的顺序无关。
- 唯一性:HashSet中的元素是唯一的,即同一个元素在HashSet中只会存储一次。
- 基于哈希表实现:HashSet是基于哈希表实现的,这就意味着HashSet中的元素必须实现hashCode()和equals()方法,以便进行快速查找。
- 元素可以为null:HashSet可以存储null元素,但只能存储一个null元素。
- 非线程安全:HashSet是非线程安全的,如果多个线程同时修改同一个HashSet,就会出现并发问题。
2.LinkedHashSet
LinkedHashSet特点
- 有序性:LinkedHashSet中的元素是有序的,即元素在LinkedHashSet中的位置与插入的顺序相同。
- 唯一性:LinkedHashSet中的元素是唯一的,即同一个元素在LinkedHashSet中只会存储一次。
- 基于哈希表+链表实现:LinkedHashSet同时保留了哈希表的高效性和链表的有序性,使用哈希表来实现元素的查找,使用链表来维护元素的插入顺序。
- 元素可以为null:LinkedHashSet可以存储null元素,但只能存储一个null元素。
- 非线程安全:LinkedHashSet是非线程安全的,如果多个线程同时修改同一个LinkedHashSet,就会出现并发问题。
3.TreeSet
TreeSet 是一个有序的集合,它的作用是提供有序的Set集合。TreeSet是基于TreeMap实现的。TreeSet中的元素支持2种排序方式:自然排序 或者 根据创建TreeSet 时提供的 Comparator 进行排序。
自然排序:排序的实体类实现java.lang.Comparable接口
HashSet<stus> s = new HashSet<>();
s.add(new stus(1, "xw", 100));
s.add(new stus(2, "wh", 101));
s.add(new stus(3, "zs", 98));
s.add(new stus(4, "ls", 34));
s.add(new stus(5, "xw", 18));
//定义一个treeset用于排序
TreeSet<stus> t=new TreeSet<>();
for (stus stus : s) {
t.add(stus);//将数据加到集合
}
for (stus stus : t) {
System.out.println(stus);//输出结果
}
注意:一定要实现Comparable接口,否则会报
com.zking.set.xxx cannot be cast to java.lang.Comparable
并且重写compareTo方法
@Override
public int compareTo(stus o) {
return this.price - o.price;
}
TreeSet构造器使用实现java.util.Comparator的匿名内部类
HashSet<stus> s = new HashSet<>();
s.add(new stus(1, "xw", 18));
s.add(new stus(2, "wh", 101));
s.add(new stus(3, "zs", 98));
s.add(new stus(4, "ls", 34));
s.add(new stus(5, "xw", 18));
/**
* TreeSet构造器使用实现java.util.Comparator的匿名内部类
*
* 如果价格一样根据编号升序
*/
// 定义一个treeset用于排序
TreeSet<stus> t = new TreeSet<>(new Comparator<stus>() {
@Override
public int compare(stus o1, stus o2) {
/**如果价格一样再进行新一轮比较
* 返回值:
* 负数,表示当前对象比另一个对象小;
* 零,表示当前对象和另一个对象相等;
* 正数,表示当前对象比另一个对象大。
*/
if (o1.getPrice() - o2.getPrice() == 0) {
return o1.getId()-o2.getId();
}
return o2.getPrice() - o1.getPrice();
}
});
for (stus stus : s) {
t.add(stus);// 将数据加到集合
}
for (stus stus : t) {
System.out.println(stus);// 输出结果
}
注意:Comparable是我和谁比,Comparator是谁和谁比。
直接return可能会出现精度丢失问题。个人建议使用if判断。
比较者大于被比较者返回正数
比较者等于被比较者返回零
比较者小于被比较者返回负数
看到这里就给个三连再走吧💖💖