JDK版本
186.png
HashSet簡介
HashSet特點
- 非線程安全
- 允許null值
- 添加值得時候會先獲取對象的hashCode方法凿渊,如果hashCode 方法返回的值一致撼短,則再調(diào)用equals方法判斷是否一致,如果不一致才add元素侧蘸。
注意: 對于HashSet中保存的對象钦幔,請注意正確重寫其equals和hashCode方法豆胸,以保證放入的對象的唯一性。
HashSet源碼
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
{
static final long serialVersionUID = -5024744406713321676L;
//底層使用HashMap來保存HashSet中所有元素赤套。
private transient HashMap<E,Object> map;
//定義一個虛擬的Object對象作為HashMap的value飘痛,將此對象定義為static final。
private static final Object PRESENT = new Object();
/**
* 默認的無參構(gòu)造器容握,構(gòu)造一個空的HashSet宣脉。
*
* 實際底層會初始化一個空的HashMap,并使用默認初始容量為16和加載因子0.75剔氏。
*/
public HashSet() {
map = new HashMap<>();
}
/**
* 構(gòu)造一個包含指定collection中的元素的新set塑猖。
*
* 實際底層使用默認的加載因子0.75和足以包含指定
* collection中所有元素的初始容量來創(chuàng)建一個HashMap。
* @param c 其中的元素將存放在此set中的collection谈跛。
*
* @throws NullPointerException if the specified collection is null
*/
public HashSet(Collection<? extends E> c) {
map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}
/**
* 以指定的initialCapacity和loadFactor構(gòu)造一個空的HashSet羊苟。
*
* 實際底層以相應的參數(shù)構(gòu)造一個空的HashMap。
* @param initialCapacity 初始容量感憾。
* @param loadFactor 加載因子蜡励。
* @throws IllegalArgumentException if the initial capacity is less
* than zero, or if the load factor is nonpositive
*/
public HashSet(int initialCapacity, float loadFactor) {
map = new HashMap<>(initialCapacity, loadFactor);
}
/**
* 以指定的initialCapacity構(gòu)造一個空的HashSet。
*
* 實際底層以相應的參數(shù)及加載因子loadFactor為0.75構(gòu)造一個空的HashMap阻桅。
* @param initialCapacity 初始容量巍虫。
* @throws IllegalArgumentException if the initial capacity is less
* than zero
*/
public HashSet(int initialCapacity) {
map = new HashMap<>(initialCapacity);
}
/**
* 以指定的initialCapacity和loadFactor構(gòu)造一個新的空鏈接哈希集合。
* 此構(gòu)造函數(shù)為包訪問權限鳍刷,不對外公開占遥,實際只是是對LinkedHashSet的支持。
*
* 實際底層會以指定的參數(shù)構(gòu)造一個空LinkedHashMap實例來實現(xiàn)输瓜。
* @param initialCapacity 初始容量瓦胎。
* @param loadFactor 加載因子芬萍。
* @param dummy 標記。搔啊!
* @throws IllegalArgumentException if the initial capacity is less
* than zero, or if the load factor is nonpositive
*/
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<>(initialCapacity, loadFactor);
}
/**
* 返回對此set中元素進行迭代的迭代器柬祠。返回元素的順序并不是特定的。
*
* 底層實際調(diào)用底層HashMap的keySet來返回所有的key负芋。
* 可見HashSet中的元素漫蛔,只是存放在了底層HashMap的key上,
* value使用一個static final的Object對象標識旧蛾。
* @return 對此set中元素進行迭代的Iterator莽龟。
* @see ConcurrentModificationException
*/
public Iterator<E> iterator() {
return map.keySet().iterator();
}
/**
* 返回此set中的元素的數(shù)量(set的容量)。
*
* 底層實際調(diào)用HashMap的size()方法返回Entry的數(shù)量锨天,就得到該Set中元素的個數(shù)毯盈。
* @return 此set中的元素的數(shù)量(set的容量)。
*/
public int size() {
return map.size();
}
/**
* 如果此set不包含任何元素病袄,則返回true搂赋。
*
* 底層實際調(diào)用HashMap的isEmpty()判斷該HashSet是否為空。
* @return 如果此set不包含任何元素益缠,則返回true脑奠。
*/
public boolean isEmpty() {
return map.isEmpty();
}
/**
* 如果此set包含指定元素,則返回true幅慌。
* 更確切地講捺信,當且僅當此set包含一個滿足(o==null ? e==null : o.equals(e))
* 的e元素時,返回true欠痴。
*
* 底層實際調(diào)用HashMap的containsKey判斷是否包含指定key迄靠。
* @param o 在此set中的存在已得到測試的元素。
* @return 如果此set包含指定元素喇辽,則返回true掌挚。
*/
public boolean contains(Object o) {
return map.containsKey(o);
}
/**
* 如果此set中尚未包含指定元素,則添加指定元素菩咨。
* 更確切地講吠式,如果此 set 沒有包含滿足(e==null ? e2==null : e.equals(e2))
* 的元素e2,則向此set 添加指定的元素e抽米。
* 如果此set已包含該元素特占,則該調(diào)用不更改set并返回false。
*
* 底層實際將將該元素作為key放入HashMap云茸。
* 由于HashMap的put()方法添加key-value對時是目,當新放入HashMap的Entry中key
* 與集合中原有Entry的key相同(hashCode()返回值相等,通過equals比較也返回true)标捺,
* 新添加的Entry的value會將覆蓋原來Entry的value懊纳,但key不會有任何改變揉抵,
* 因此如果向HashSet中添加一個已經(jīng)存在的元素時,新添加的集合元素將不會被放入HashMap中嗤疯,
* 原來的元素也不會有任何改變冤今,這也就滿足了Set中元素不重復的特性。
* @param e 將添加到此set中的元素。
* @return 如果此set尚未包含指定元素,則返回true减响。
*/
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
/**
* 如果指定元素存在于此set中,則將其移除龟糕。
* 更確切地講,如果此set包含一個滿足(o==null ? e==null : o.equals(e))的元素e凑术,
* 則將其移除。如果此set已包含該元素所意,則返回true
* (或者:如果此set因調(diào)用而發(fā)生更改淮逊,則返回true)。(一旦調(diào)用返回扶踊,則此set不再包含該元素)泄鹏。
*
* 底層實際調(diào)用HashMap的remove方法刪除指定Entry。
* @param o 如果存在于此set中則需要將其移除的對象秧耗。
* @return 如果set包含指定元素备籽,則返回true。
*/
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}
/**
* 從此set中移除所有元素分井。此調(diào)用返回后车猬,該set將為空。
*
* 底層實際調(diào)用HashMap的clear方法清空Entry中所有元素尺锚。
*/
public void clear() {
map.clear();
}
/**
* 返回此HashSet實例的淺表副本:并沒有復制這些元素本身珠闰。
*
* 底層實際調(diào)用HashMap的clone()方法,獲取HashMap的淺表副本瘫辩,并設置到 HashSet中伏嗜。
* @return a shallow copy of this set
*/
@SuppressWarnings("unchecked")
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);
}
}
/**
* Save the state of this <tt>HashSet</tt> instance to a stream (that is,
* serialize it).
*
* @serialData The capacity of the backing <tt>HashMap</tt> instance
* (int), and its load factor (float) are emitted, followed by
* the size of the set (the number of elements it contains)
* (int), followed by all of its elements (each an Object) in
* no particular order.
*/
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException {
// Write out any hidden serialization magic
s.defaultWriteObject();
// Write out HashMap capacity and load factor
s.writeInt(map.capacity());
s.writeFloat(map.loadFactor());
// Write out size
s.writeInt(map.size());
// Write out all elements in the proper order.
for (E e : map.keySet())
s.writeObject(e);
}
/**
* Reconstitute the <tt>HashSet</tt> instance from a stream (that is,
* deserialize it).
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
// Read in any hidden serialization magic
s.defaultReadObject();
// Read capacity and verify non-negative.
int capacity = s.readInt();
if (capacity < 0) {
throw new InvalidObjectException("Illegal capacity: " +
capacity);
}
// Read load factor and verify positive and non NaN.
float loadFactor = s.readFloat();
if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
throw new InvalidObjectException("Illegal load factor: " +
loadFactor);
}
// Read size and verify non-negative.
int size = s.readInt();
if (size < 0) {
throw new InvalidObjectException("Illegal size: " +
size);
}
// Set the capacity according to the size and load factor ensuring that
// the HashMap is at least 25% full but clamping to maximum capacity.
capacity = (int) Math.min(size * Math.min(1 / loadFactor, 4.0f),
HashMap.MAXIMUM_CAPACITY);
// Constructing the backing map will lazily create an array when the first element is
// added, so check it before construction. Call HashMap.tableSizeFor to compute the
// actual allocation size. Check Map.Entry[].class since it's the nearest public type to
// what is actually created.
SharedSecrets.getJavaOISAccess()
.checkArray(s, Map.Entry[].class, HashMap.tableSizeFor(capacity));
// Create backing HashMap
map = (((HashSet<?>)this) instanceof LinkedHashSet ?
new LinkedHashMap<E,Object>(capacity, loadFactor) :
new HashMap<E,Object>(capacity, loadFactor));
// Read in all elements in the proper order.
for (int i=0; i<size; i++) {
@SuppressWarnings("unchecked")
E e = (E) s.readObject();
map.put(e, PRESENT);
}
}
/**
* Creates a <em><a href="Spliterator.html#binding">late-binding</a></em>
* and <em>fail-fast</em> {@link Spliterator} over the elements in this
* set.
*
* <p>The {@code Spliterator} reports {@link Spliterator#SIZED} and
* {@link Spliterator#DISTINCT}. Overriding implementations should document
* the reporting of additional characteristic values.
*
* @return a {@code Spliterator} over the elements in this set
* @since 1.8
*/
public Spliterator<E> spliterator() {
return new HashMap.KeySpliterator<E,Object>(map, 0, -1, 0, 0);
}
}