通過之前對其他的集合的再讀及整理,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;
// 虛擬對象
private static final Object PRESENT = new Object();
// 無參構造
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);
}
// 由於默認修飾符,同包可訪問,我們用不到,在LinkedLinkedHashMap中有具體應用,後期會講解
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<>(initialCapacity, loadFactor);
}
}
可以很容易看出來,HashSet 底層其實就是維護了一個 HashMap,所以 默認長度爲 16,負載因子爲 0.75f,樹化閥值爲8,反樹化閥值爲6,等等全是HashMap的特性。那麼他是怎麼實現無序 不可重複的特性的呢? 重點還在於虛擬對象 PRESENT ,我們從add 方法中可以確定:
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
在添加新的元素時,以新的元素作爲key , 虛擬對象 作爲 value 。因爲每次添加同一個虛擬對象,所以 只有保持 key的不同才能保證唯一,由此實現了 不可重複的特性。而HashMap 本身是通過 key的hash值來確定存放的位置,自然就是無序。
在刪除的方法中也是同樣的邏輯,找到對應的key 直接把該位置元素刪除,如果是鏈表或者是樹,道理相通,具體可以參考HashMap。
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}
HashSet 相對較爲簡單,其他的方法也是一樣,此時就不一一解析了。