概述
Java 集合接口听得最多的应该就是 List、Queue、Set、Map。
Set 的实现类重要的不多,而且源码也不复杂,甚至觉得没啥可讲的。
这一篇就汇总贴一下源码注释吧,也不过多介绍了: HashSet、TreeSet
还是老规矩,先把源码放上:
- Set 接口源码:https://github.com/qianwei4712/JDK1.8.0.25-read/blob/master/src/main/java/java/util/Set.java
- HashSet 源码:https://github.com/qianwei4712/JDK1.8.0.25-read/blob/master/src/main/java/java/util/HashSet.java
- TreeSet 源码:https://github.com/qianwei4712/JDK1.8.0.25-read/blob/master/src/main/java/java/util/TreeSet.java
Set 接口
首先说下 Set 接口设计的定义:
一个不包含重复元素的容器,即不满足 e1.equals(e2) 条件。
Set 接口的官方注释中有这么一句: 如果允许 null,只允许存在一个 null
作为一个接口,这里就列一下它的方法和设计要求,具体使用在实现类里讲解。
查询操作
方法名/参数/返回值 | 接口设计约束 |
---|---|
int size(); | 返回此集合中的元素数。如果此集包含超过 Integer.MAX_VALUE,则返回 Integer.MAX_VALUE。 |
boolean isEmpty(); | 如果集合不包含任何元素,则返回true |
boolean contains(Object o); | 如果此集合包含指定的元素,则返回 true。 更正式地讲,当且仅当此集合包含一个元素 e,使得(o == null?e == null:o 时,才返回 true)。 |
Iterator iterator(); | 返回此集合中元素的迭代器。元素以不特定的顺序返回(除非此集合是提供保证的某些类的实例)。 |
Object[] toArray(); T[] toArray(T[] a); |
转数组 |
修改操作
方法名/参数/返回值 | 接口设计约束 |
---|---|
boolean add(E e); | 如果指定的元素尚不存在,则将其添加到该集合(可选操作) 更正式地讲,如果集合中不包含任何元素 e2 ,满足e == null?e2 == null :e.equals(e2) 如果此集合已经包含元素,则调用将使该集合保持不变,并返回 false 结合构造函数上的限制,可以确保集合永远不会包含重复元素 |
boolean remove(Object o); | 如果存在指定的元素,则从该集合中删除(可选操作) 更正式地讲,删除元素 e,满足o == null?e == null:o.equals(e) 如果此集合包含元素,则返回 true(或者等效地,如果此集合作为调用结果更改) 一旦调用返回,此集合将不包含该元素。 |
其他操作
方法名/参数/返回值 | 接口设计约束 |
---|---|
boolean containsAll(Collection<?> c); | 判断包含全部 |
boolean addAll(Collection<? extends E> c); | 批量添加 |
boolean retainAll(Collection<?> c); | 删除差集 |
boolean removeAll(Collection<?> c); | 删除交集 |
void clear(); | 删除所有元素,操作后集合为空 |
boolean equals(Object o); | 两个集合具有相同的大小,指定集合的每个成员都包含在此集合中,则返回 true 此定义确保 equals方法可在set接口的不同实现中正常工作 |
int hashCode(); | 返回此集合的哈希码值 集合的哈希码定义为集合中元素的哈希码之和,其中null元素的哈希码为零。 |
HashSet 源码
HashSet 通过内部维护一个 HashMap 来实现。主要就以下几点:
- 添加到 HashSet 的元素作为 HashMap 的 Key
- 所有 HashMap 的 Value 共用一个 static final 的 new Object
HashMap源码可以看看以前写过的 : https://blog.csdn.net/m0_46144826/article/details/106300438
实在没什么好讲的,贴下代码略过了。。。。
/**
* 此类实现 Set接口,并由哈希表(实际上是 HashMap实例)支持。
* 它不保证集合的迭代顺序,特别是,它不能保证顺序会随着时间的推移保持恒定。此类允许null元素。<p>
*
* 此类为基本操作提供恒定的时间性能(添加,删除,包含和大小),假设哈希函数将元素正确分散在各个存储桶中。
* 对此集合进行迭代需要的时间与 HashSet实例的大小(元素数)之和加上 HashMap实例的“容量”(数量之和)成比例个桶。
* 因此,如果迭代性能很重要,则不要将初始容量设置得过高(或负载因数过低),这一点非常重要。<p>
*
* <strong>请注意,此实现未同步。</ strong>
* 如果多个线程同时访问 hashset,并且线程中的至少一个修改了哈希集,则必须外部同步。
* 这通常是通过对某些自然封装了该对象的对象进行同步来完成的。<p>
*
* 如果不存在这样的对象,则应使用{@link Collections#synchronizedSet Collections.synchronizedSet} 方法来“包装”该集合。
* 最好在创建时执行此操作,以防止意外异步访问集合:
* <pre> * Set s = Collections.synchronizedSet(new HashSet(...)); </ pre><p>
*
*此类的iterator方法返回的迭代器为<i>fail-fast</ i>:
* 如果在创建迭代器后的任何时间修改了集合,则除通过迭代器自己的remove法外,迭代器将引发{@link ConcurrentModificationException}。<p>
*
* 请注意,不能保证迭代器的快速失败行为,因为通常来说,在存在不同步的并发修改的情况下,不可能做出任何硬性保证。
* 快速失败的迭代器尽最大努力抛出<tt> ConcurrentModificationException </ tt>。
* 因此,编写依赖于此异常的程序的正确性是错误的:<i>迭代器的快速失败行为仅应用于检测错误。</ i>
*
* @param <E> 此集合所维护的元素类型
* @since 1.2
*/
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
{
static final long serialVersionUID = -5024744406713321676L;
private transient HashMap<E,Object> map;
// 在 HashMap中与key对应的虚拟值,所有 HashSet公用一个
private static