前言
1.为什么要使用Set集合?
1.1.去重
Set集合中的元素是不重复的,如果我们需要去除一个列表或元组中的重复元素,就可以转换为set类型来达到去重的目的。
1.2.提高查找速度
由于Set集合是通过哈希表实现的,所以查找元素的时间复杂度为O(1),相对于列表或元组来说,它的查找速度更快。
1.3.数学运算
集合论是计算机科学中的重要分支,Set集合可以用于实现集合论中的各种运算,如并集、交集、差集等。
2.使用场景
在公司内有可能要用别人的接口,而别人对于传过来的数据可能是有重复或者杂乱无章的,因为不是我们的数据库不能进行sql语句的编辑,达到自己想要的效果,那么就可以用到set集合,从而达到我们的去重复和排序的需求。
一、Set集合
Set特点
1.1无序:元素输入的顺序与插入的顺序不一致。
Set<Integer> set=new HashSet<Integer>(); set.add(3); set.add(0); set.add(2); set.add(3); System.out.println(set);
1.2元素唯一:不允许添加重复的元素。
1.3无下标
Set<Integer> set=new HashSet<Integer>(); set.add(2); set.add(0); set.add(2); set.add(3); set.add(6); System.out.println(set);
由于Set集合中的数据是唯一的,那么Set集合是怎么让它里面的元素不重复呢?
- 判断重复元素的时候,Set集合会调用hashCode()和equal()方法来实现;而在没有重写equals方法之前默认比较的是Object(引用类型),实际上比较的是内存地址。
- 【先比较hashcode值是否相同,再比较equals】
- 比较对象的属性相同时,已存在的对象信息不会被覆盖,而是过滤了
如果添加重复元素是覆盖?还是过滤?怎么实现去除重复的?
实战操作:
//Set存储字符串可以去重,那么存储对象呢? HashSet<stu> s=new HashSet<stu>(); s.add(new stu(1, "aa")); s.add(new stu(2, "bb")); //是否包含 boolean contains = s.contains(new stu(1, "aa")); System.out.println(contains); // 这里打印结果是false,不包含。 } }
这里发现同样的名字居然可以重复存储,那这不是违背了Set的特点吗?其实不然,因为对象是new出来的,在内存上开辟了一个新的内存地址,如果不重写equals方法和hashCode方法默认比较的就是内存地址;
⚠注意:如果只重写equals方法,默认还是比较内存地址,需要equals方法和hashCode方法一起重写,才能达到去除重的效果,当我hashCode一致时候,会进行equals比较。如果equals返回的是true,那说明一致,则不会添加,也就是过滤掉了。
三、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, "aa", 100)); s.add(new stus(2, "bb", 101)); s.add(new stus(3, "cc", 98)); s.add(new stus(4, "dd", 34)); s.add(new stus(5, "ee", 18)); //定义一个treeset用于排序 TreeSet<stus> tr=new TreeSet<>(); for (stus stus : s) { tr.add(stus);//将数据加到集合 } for (stus stus : tr) { 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, "aa", 18)); s.add(new stus(2, "bb", 101)); s.add(new stus(3, "cc", 98)); s.add(new stus(4, "dd", 34)); s.add(new stus(5, "ee", 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.compareTo(o2); } return o2.getPrice() - o1.getPrice(); } }); for (stus stus : s) { t.add(stus);// 将数据加到集合 } for (stus stus : t) { System.out.println(stus);// 输出结果 }
注意:Comparable是我和谁比,Comparator是谁和谁比。
直接return可能会出现精度丢失问题。个人建议使用if判断。
比较者大于被比较者返回正数
比较者等于被比较者返回零
比较者小于被比较者返回负数
本期介绍就到这.......😜😜😜😜😜😜😜