HashSet源码解析
由于HashSet的本质是一个HashMap,一些详细的方法分析这里就不在讲述了,参考HashMap
1.继承体系
HashSet也是Collection旗下的一个重要的类,更详细的说它是Set接口的一个重要实现类,其存储元素无序不可重复
,其底层实现使用的是HashMap
2.核心属性
/*
* 因为HashSet底层本质上是一个HashMap,就是这个map属性
*/
private transient HashMap<E,Object> map;
/*
* Set是一个单列集合,而Map是双列映射,HashSet使用的是Map的key属性,
* 而所有的value都是这个PRESENT,是一个Object对象。
*/
private static final Object PRESENT = new Object();
3.构造方法
通过下面的构造方法,可以找到HashSet本质上就是一个HashMap
,使用的是HashMap的key列来存储元素。
/*
* 底层调用HashMap的无参构造器创建了一个HashMap对象,为map属性赋值。
* 而HashMap的无参构造器只会为内部的加载因子赋值为0.75,不会初始化哈希表。
*/
public HashSet() {
map = new HashMap<>();
}
/*
* 传入一个集合。
*/
public HashSet(Collection<? extends E> c) {
map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}
/*
* 传入初始容量和加载因子。
*/
public HashSet(int initialCapacity, float loadFactor) {
map = new HashMap<>(initialCapacity, loadFactor);
}
/*
* 传入初始容量。
*/
public HashSet(int initialCapacity) {
map = new HashMap<>(initialCapacity);
}
4.常用方法
由于Set是无序的(底层是hashMap),所有没有像List那样的get()方法,只能通过迭代器遍历。
4.1add()
public boolean add(E e) {
/*
* 调用的是HashMap的put()方法,key就是e,value都是PRESENT(Object对象)
* 也即map中所有的value都是PRESENT。
*/
return map.put(e, PRESENT)==null;
}
4.2remove()
/*
* 调用的是HashMap的remove()方法,删除元素
*/
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}
4.3contains()
//判断Set是否存在指定元素
public boolean contains(Object o) {
//调用的是map的containsKey()方法。
return map.containsKey(o);
}
4.4size()
//查看set中的元素个数
public int size() {
//返回map的size()
return map.size();
}
4.5isEmpty()
//判断set中是否有元素
public boolean isEmpty() {
return map.isEmpty();
}
5.LinkedHashSet
5.1源码解析
LinkedHashSet继承了HashSet,其增删改查等方法使用的都是HashSet的方法。
package java.util;
// LinkedHashSet继承自HashSet
public class LinkedHashSet<E>
extends HashSet<E>
implements Set<E>, Cloneable, java.io.Serializable {
private static final long serialVersionUID = -2851667679971038690L;
// 传入容量和装载因子
public LinkedHashSet(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor, true);
}
// 只传入容量, 装载因子默认为0.75
public LinkedHashSet(int initialCapacity) {
super(initialCapacity, .75f, true);
}
// 使用默认容量16, 默认装载因子0.75
public LinkedHashSet() {
super(16, .75f, true);
}
// 将集合c中的所有元素添加到LinkedHashSet中
// HashSet中使用的是Math.max((int) (c.size()/.75f) + 1, 16)
public LinkedHashSet(Collection<? extends E> c) {
super(Math.max(2*c.size(), 11), .75f, true);
addAll(c);
}
}
可以看出,LinkedHashSet的所有构造器调用的都是HashSet的同一个构造器
/*
* 调用的都是这个构造器,可以看出底层是new了一个LinkedHashMap。
* 通过前面的LinkedHashMap的源码解析,我们知道其是可以实现LRU机制的,
* 前提是accessOrder = true。接下来我们看一下LinkedHashSet可不可以实现。
*/
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<>(initialCapacity, loadFactor);
}
// ||
// ||
// \/
/*
* 上面的构造器调用的是LinkedHashMap的这个构造器,其accessOrder是固定的为false
* 即不能实现LRU,链表只能按照元素的添加顺序进行排序。
*/
public LinkedHashMap(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
//accessOrder固定为false,不能为其赋值,即不支持LRU。
accessOrder = false;
}
5.2总结
LinkedHashSet底层使用的是LinkedHashMap
,其不支持LRU机制,其内部的链表只能按照元素添加的顺序排序
,