java中的集合(一)Set

本文详细介绍了Set集合在Java中的作用,包括Set的无序性、唯一性保证、线程安全的CopyOnWriteArraySet以及其实现机制。重点讲解了HashSet和TreeSet的区别,以及为何使用Set而非List进行去重。

1、什么是set?

set是继承于collection的一种集合。他和list一样也是能保存多个相同class的实例的集合工具。

2、为什么会出现Set?

对于大多数的需求来说,大家使用list存放元素基本都够用了,那么为什么有出现了Set呢?当我们需要一个集合内部不允许重复数据出现的时候,我们往往需要遍历这个list然后做去重操作,因此java给出了一个新的集合,这个集合不保证存取的顺序,但是可以保证数据的唯一性。

3、Set的特点

a、Set的设计是无序的,那么为什么Set是无序的呢?

其实,Set的底层是基于Map来实现的,而Map底层在1.8以前采用数组+链表的结构来存储,1.8后又是采用的数组+链表+红黑树来存储,这样子数据具体存放在数组上的哪个链表或者红黑树的哪个位置就和放入顺序无关了。因此说其是无序的(这里的无序是指,放入和取出不是相同,如第一个放入的,取到的第一个不一定会是第一个放入的)

但是有一个子类TreeSet是有序的。这里的有序指的是自然排序,一般就是说是小的放在前面这种,和我们所说的有序也不一样。

b、Set是保证唯一性的,那么Set是怎么保证唯一性的呢?

这里两种不同的子类有不同的实现方式

1、HashSet,对于HashSet来说,通过比较对象的HashCode就能解决大多数问题,然后如果HashCode相同,就可以通过对象的equals方法来对比是否是相同,这两步走完如果还是相同那么就会认为这两个是一个对象就不会将后面这个加入HashSet中

2、TreeSet,对于HashSet来说,需要调用者传入一个对象的比较器,那么你的比较器比较结果是相同就不会加入,从而保证其唯一性

c、Set是不能为空的,为什么不能为null呢?

对于Set来说,实现了HashCode(),以及equals()。这两个方法如果使用null调用都会产生空指针异常,因此Set不允许NULL值的存在。当然整个Set是null还是可以的,因此,Set也是要做空指针异常的处理的。

4、线程安全问题

对于一个集合类来说,他的添加方法都会有多线程异常的隐患存在,那么对于Set来说,有什么子类是线程安全的么?那么,java提供了一个CopyOnWriteArraySet,这个类就是线程安全的

5、CopyOnWriteArraySet实现线程安全的原理

对于CopyOnWriteArraySet来说底层基于CopyOnWriteArrayList来实现,因此我们将在讲List时来讲这个。 特别注意的点是,集合类说的线程安全仅仅代表其单个方法是线程安全,在使用的外部的线程安全还需要调用者自行处理。

### JavaSet 集合概述 #### Set 接口特性 SetJava 集合框架中的个重要接口,其主要特点是不允许存储重复元素[^2]。这使得 Set 类似于数学上的集合概念,在实际应用中非常有用。 #### 实现类及其特点 - **HashSet** - 基于哈希表实现,内部通常使用 `HashMap` 来管理数据[^1]。 - 提供常数时间复杂度 O(1) 的性能用于基本操作(add, remove 和 contains),假设哈希函数分配得当。 - **LinkedHashSet** - 继承自 HashSet 并保持插入顺序不变。 - 同样提供高效的存取速度,但在迭代访问时会按照元素被添加到集合中的先后次序返回。 - **TreeSet** - 底层基于红黑树实现,能够自动对元素进行排序。 - 支持自然排序或通过 Comparator 定制排序逻辑。 - 插入、删除和查找的时间复杂度均为 O(log n)。 #### 数据结构与应用场景 对于不同类型的 Set 实现: - 当需要快速的成员测试而不在乎顺序时可以选择 `HashSet`. - 如果希望保留元素加入集合时的顺序,则应考虑使用 `LinkedHashSet`. - 对有序性的需求较高,比如要频繁获取最小/最大值的情况下推荐选用 `TreeSet`. #### 示例代码展示常见操作 下面给出段简单的例子来说明如何创建 Sets 及执行些基础的操作如求交集和差集: ```java import java.util.HashSet; import java.util.Set; public class Example { public static void main(String[] args) { // 创建两个整型的 HashSet Set<Integer> setA = new HashSet<>(); Set<Integer> setB = new HashSet<>(); // 添加元素至第集合 setA.add(1); setA.add(2); setA.add(3); // 添加元素至第二个集合 setB.add(2); setB.add(3); setB.add(4); // 计算 A ∩ B (即两者的公共部分) Set<Integer> intersection = new HashSet<>(setA); intersection.retainAll(setB); System.out.println("Intersection of sets is: " + intersection); // 计算 A - B (即只存在于前者而不属于后者的部分) Set<Integer> differenceAB = new HashSet<>(setA); differenceAB.removeAll(setB); System.out.println("Elements only in first set are: " + differenceAB); // 计算 B - A Set<Integer> differenceBA = new HashSet<>(setB); differenceBA.removeAll(setA); System.out.println("Elements only in second set are: " + differenceBA); } } ``` 这段程序展示了怎样利用 `retainAll()` 函数计算两个集合之间的交集,以及运用 `removeAll()` 方法找出差异项[^3].
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值