java 集合 —— Set 實現類之 HashSet
阿新 • • 發佈:2019-01-25
原始碼基於jdk 1.7.81
HashSet 簡介
HashSet 是一個元素不能重複的集合。
HashSet 中當新增的元素有重複時,新增失敗。
HashSet 是 Set 的一個實現類,而 Set 又繼承了Collection 方法,並且沒有新增多餘的方法。
HashSet 繼承了AbstractSet 類。
實現了 Cloneable 介面,說明它重寫了clone()方法,可以被克隆。
實現了 java.io.Serializable 介面,說明它可以被序列化。
public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable
HashSet 成員變數
static final long serialVersionUID = -5024744406713321676L;
private transient HashMap<E,Object> map;
private static final Object PRESENT = new Object();
map 儲存的是鍵值對,在這裡我們只用到了它的 key ,Object 是用來給它的 value 賦值的。所以 HashSet 擁有 HashMap 的所有特性,是用陣列加連結串列的資料結構,並且每個 key 值是唯一的,在 HashSet 裡體現的是它的值使唯一的。
HashSet 的建構函式
//預設建構函式 public HashSet() { map = new HashMap<E,Object>(); } //儲存Collection 裡面所有的資料 public HashSet(Collection<? extends E> c) { // 建立map。 // 為什麼要呼叫Math.max((int) (c.size()/.75f) + 1, 16),從 (c.size()/.75f) + 1 和 16 中選擇一個比較大的樹呢? // 首先,說明(c.size()/.75f) + 1 // 因為從HashMap的效率(時間成本和空間成本)考慮,HashMap的載入因子是0.75。 // 當HashMap的“閾值”(閾值=HashMap總的大小*載入因子) < “HashMap實際大小”時, // 就需要將HashMap的容量翻倍。 // 所以,(c.size()/.75f) + 1 計算出來的正好是總的空間大小。 // 接下來,說明為什麼是 16 。 // HashMap的總的大小,必須是2的指數倍。若建立HashMap時,指定的大小不是2的指數倍; // HashMap的建構函式中也會重新計算,找出比“指定大小”大的最小的2的指數倍的數。 // 所以,這裡指定為16是從效能考慮。避免重複計算。 map = new HashMap<E,Object>(Math.max((int) (c.size()/.75f) + 1, 16)); //增加 c 的元素在 map 裡。 addAll(c); } //自動義容量和載入因子。 public HashSet(int initialCapacity, float loadFactor) { map = new HashMap<E,Object>(initialCapacity, loadFactor); } //自定義容量 public HashSet(int initialCapacity) { map = new HashMap<E,Object>(initialCapacity); } //自動義容量和載入因子。 HashSet(int initialCapacity, float loadFactor, boolean dummy) { map = new LinkedHashMap<E,Object>(initialCapacity, loadFactor); }
HashSet 的構造方法都是呼叫 HashMap 的構造方法,學習HashSet 的時候可以對照著HashMap 來學習。
HashSet 的迭代器
//返回HashSet的迭代器
public Iterator<E> iterator() {
return map.keySet().iterator();
}
HashSet 的迭代器是呼叫了HashMap 通過鍵遍歷的迭代器。
HashSet 的基本操作
/*
* 不能重複
* 去重
* key 允許為 null
* 執行緒不安全
*/
public boolean add(E e) {
return map.put(e, PRESENT)==null;//呼叫了HashMap的add方法
}
public V put(K key, V value) {
//當 key 為 null
if (key == null)
return putForNullKey(value);
//得到 hash 值
int hash = hash(key.hashCode());
//獲得索引
int i = indexFor(hash, table.length);
//遍歷索引位置的連結串列
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
//如果連結串列中已經存在當前的key,覆蓋即可
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
//如果當前連結串列沒有就增加這個鍵值對到集合中
addEntry(hash, key, value, i);
return null; // 當原來沒有這個元素時,返回null.
}
當被新增的元素已經存在在集合中,put 方法返回的 是原來的 value 值,在add 方法中,put 方法返回的 oldvalue 值 != null ,返回false,則新增失敗。
當被新增的元素不存在集合中,新增元素在map 裡,返回 null。