Collection系列
- HashMap --> HashMap源码解析_yolan6824的博客-CSDN博客
- LinkedHashMap --> LinkedHashMap源码解析_yolan6824的博客-CSDN博客
- HashSet --> 本篇
先导
我们知道,HashSet可以用来存储不同的元素。即使add两个相同的元素,在HashSet里面也只会保存其中的一个。所以HashSet可以用来去重。
看到这里,我们会产生一个问题:
- HashSet是怎么去重的?它是依据什么去重的?
实现
HashSet基本结构
看一下HashSet的基本结构:
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;
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
public HashSet() {
map = new HashMap<>();
}
}
很容易可以看出,HashSet持有一个HashMap的实例。
至此可以猜测一下HashSet就是依靠的HashMap执行的去重,因为HashMap的key是唯一的。那么回忆一下,HashMap的key是如何保持唯一的?
当HashMap执行put操作的时候,会根据key的hashcode()求出它在HashMap维护的数组中的index。HashMap是依据 链表/红黑树 解决哈希冲突的,所以遍历该index对应的 链表/红黑树 即可以找到这个key。如果找到这个key,就直接替换key对应的value。否则,新建一个Node。
经历了查找,查找不到再新建的步骤,自然就可以保持HashMap中的key唯一的特性了。
所以可以大胆猜测,HashSet就是利用了HashMap中的key唯一的特性,实现了自己的数据结构。
HashSet方法实现
知道了HashSet的基本数据结构,下面考究下HashSet的一些基本方法来验证一下自己的猜想。
// HashSet.java
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}
public boolean contains(Object o) {
return map.containsKey(o);
}
public void clear() {
map.clear();
}
没错,,,,HashSet的方法真的如此简洁,,,
add
add方法简单调了HashMap.put方法,key为传入的参数,即验证了上面我们的猜想,HashSet就是利用了HashMap的key实现了去重。
map.put的返回值为null,代表这个key之前存入的值为null 或 之前没有这个key。既然map对应key存入的value都是PRESENT,这就代表,HashSet.add方法返回true,代表之前HashSet中没有存有这个元素。
remove
调用HashMap.remove方法,remove()方法返回值为null,代表该key对应的value为空,或之前不存在这个key。所以HashSet.remove方法返回true,代表HashSet中之前存在过这个key。
contains
调用HashMap.containsKey。