前言:HashSet也是我们开发中常用的数据结构之一,它的特点是:无序(存储和读取的顺序有可能不一样)、不重复(要求元素唯一)、可以有null值(只能有一个)、没有索引等。其实他的底层是基于HashMap来实现的,所以在看这篇文章之前可以去翻看一下我的上一篇博客 基于jdk1.8的HashMap的源码分析。本文还是一样,会从成员属性、构造方法、常用方法来进行分析,以最简单的方式,来读懂最难的代码。
一:HashSet的具体实现
1.1:HashSet的成员属性
//使用HashMap用来存储元素
private transient HashMap<E,Object> map;
//因为HashSet只用key存储数据,所以这个变量用来向map中存储数据时代替value
private static final Object PRESENT = new Object();
1.2:HashSet的构造方法
//方法一:通过实例化HashMap来实现
public HashSet() {
map = new HashMap<>();
}
//方法二:通过传入集合来实现
public HashSet(Collection<? extends E> c) {
//通过集合大小和HashMap默认的加载因子来初始化容量,后面+1是为了向上取到整数
map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}
//添加元素
public boolean addAll(Collection<? extends E> c) {
boolean modified = false;
for (E e : c)
if (add(e))
modified = true;
return modified;
}
//方法三:通过指定容量大小和加载因子初始化HashMap来实现
public HashSet(int initialCapacity, float loadFactor) {
map = new HashMap<>(initialCapacity, loadFactor);
}
//方法四:指定容量大小初始化HashMap来实现
public HashSet(int initialCapacity) {
map = new HashMap<>(initialCapacity);
}
1.3:HashSet的常用方法
1.3.1:add(E e) 方法
//可以看出底层也是通过HashMap的put方法来实现的
//HashMap的put方法返回的是要么是旧值要么是null
//PRESENT初始化是null并且不会改变,所以这个值一直是null,因此put方法返回的就一直是null
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
1.3.2:remove(object o) 和 clear()方法
//可以看出底层也是通过HashMap的remove方法来实现的
//map的remove方法的返回的是被删除键对应的值,存储的时候是null
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}
public void clear() {
map.clear();
}
1.3.3:contains(Object o)方法
//可以看出底层也是通过HashMap的containsKey方法来实现的
public boolean contains(Object o) {
return map.containsKey(o);
}
1.3.4:isEmpty()方法
//可以看出底层也是通过HashMap的isEmpty方法来实现的
public boolean isEmpty() {
return map.isEmpty();
}
1.3.5:clone()方法
/**
* 可以看出使用的是Object的clone()方法来实现的,得到Object对象后强转成HashSet<E>
* 接着通过调用的HashMap的clone()方法把它的底层实现HashMap也克隆一份,并赋值给newSet返回
*/
public Object clone() {
try {
HashSet<E> newSet = (HashSet<E>) super.clone();
newSet.map = (HashMap<E, Object>) map.clone();
return newSet;
} catch (CloneNotSupportedException e) {
throw new InternalError(e);
}
}
二:HashSet的常见问题及总结
2.1:为什么使用HashSet?
解析:和LinkedList和ArrayList的查询速度相比,HashSet更快。通过上面分析我们知道HashSet是基于HashMap实现的,并且HashSet只存储key,value是null,当我们去获取数据的时候,就只用根据散列值计算数组下标,从而获取值,速度很快。而LinkedList是基于链表的形式,查找需要逐级遍历,效率低。ArrayList如果不知道元素的的具体位置,还是要全部遍历直到查到结果,效率一样低。
2.2:总结
- HashSet是基于HashMap实现的,不能有重复的元素;
- HashSet存储数据是无序的,存储和读取的顺序有可能不一样;
- HashSet可以插入null,但只能有一个;
- HashSet不是线程安全的;
- HashSet和LinkedList和ArrayList的查询速度相比,HashSet更快;