HashSet、LinkedHashSet和TreeSet详解(基于jdk1.8)
Set接口
Set接口是Collection接口下的子接口,Set接口下有AbstractSet实现类和SortedSet接口,不管是继承自AbstractSet还是SortedSet,我们接下来要讲的就是以下这三种集合:HashSet、LinkedHashSet、TreeSet。
Set集合的本质是基于Map实现的,因此有许多的特点是相似的。这在后面,我会做详细的解释。
Set接口下的集合不可以存储重复的数据,存储的数据是无序的,可以仅仅存储一个NULL值。Set接口下的集合都是线程不安全的。
Set接口和Map接口的底层结构是一样的,和Map的区别就是Set是存储单个值,而Map是以键值对Key-Value的形式存储数据。
HashSet
HashSet实现自AbstractSet抽象类,是基于HashMap实现Set接口的实现类。
特点:
- 不可以存储重复的数据
- 存储的数据是无序的
- 最多可以存储一个NULL值,且NULL值在第一个位置
HashSet是线程不安全的,会抛出ConcurrentModificationException异常。
HashSet源码解析
我们将从继承关系、属性、默认值、构造方法、扩容机制和常用方法等方式进行解析。
继承关系
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
由此我们可以看出HashSet是继承自AbstractSet,实现了Set接口,Cloneable接口(可克隆)和Serializable接口(可序列化)。
属性
//map属性是用来存储数据的
private transient HashMap<E,Object> map;
//Object类型的present
private static final Object PRESENT = new Object();
由此可以看出,HashSet是基于Map实现的,其中HashSet中存储的元素存放在Map中Key的位置,present填充在Map中Value的位置,这样HashSet就变成了只存放单个值的集合了。如果还不理解,那么我们先继续往下看。
构造方法
//无参构造方法,直接将上面创建的map进行实例化
public HashSet() {
map = new HashMap<>();}
//有参构造方法,参数中放入Collection接口下的集合
public HashSet(Collection<? extends E> c){
}
//有参构造方法,参数表示初始化容量
public HashSet(int initialCapacity{
map = new HashMap<(initialCapacity);}
//有参构造方法,参数表示初始容量大小、加载因子
public HashSet(int initialCapacity, float loadFactor) {
map = new HashMap<>(initialCapacity, loadFactor);}
默认值
//默认容量大小为16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
//默认加载因子
static final float DEFAULT_LOAD_FACTOR = 0.75f;