集合框架之Set集合详解

175 阅读2分钟

一、前言

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特点

  1. 无序性:HashSet中的元素是无序的,即元素在HashSet中的位置和插入的顺序无关。
  2. 唯一性:HashSet中的元素是唯一的,即同一个元素在HashSet中只会存储一次。
  3. 基于哈希表实现:HashSet是基于哈希表实现的,这就意味着HashSet中的元素必须实现hashCode()和equals()方法,以便进行快速查找。
  4. 元素可以为null:HashSet可以存储null元素,但只能存储一个null元素。
  5. 非线程安全:HashSet是非线程安全的,如果多个线程同时修改同一个HashSet,就会出现并发问题。

2.LinkedHashSet

LinkedHashSet特点

  1. 有序性:LinkedHashSet中的元素是有序的,即元素在LinkedHashSet中的位置与插入的顺序相同。
  2. 唯一性:LinkedHashSet中的元素是唯一的,即同一个元素在LinkedHashSet中只会存储一次。
  3. 基于哈希表+链表实现:LinkedHashSet同时保留了哈希表的高效性和链表的有序性,使用哈希表来实现元素的查找,使用链表来维护元素的插入顺序。
  4. 元素可以为null:LinkedHashSet可以存储null元素,但只能存储一个null元素。
  5. 非线程安全: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判断。

比较者大于被比较者返回正数

比较者等于被比较者返回零

比较者小于被比较者返回负数

看到这里就给个三连再走吧💖💖