Overview of HashSet:
HashSet implements the Set interface, which is supported by a hash table (actually a HashMap instance). It does not guarantee the iteration order of set; in particular, it does not guarantee that the order will remain constant.
This class allows null elements and is non-thread-safe.
- Since HashSet is based on HashMap, the underlying layer of HashSet uses HashMap to save all elements, so the implementation of HashSet is relatively simple. The operation of HashSet is basically completed by directly calling the relevant methods of the underlying HashMap.
- If you have read the source code of HashMap, I believe it will be very easy for you to read the source code of HashSet. If you haven't read the source code of HashMap, please don't be disappointed. Please visit my last blog: Realization Principle and Source Code Analysis of HashMap (I) and HashMap Source Analysis (2)
2. HashSet Source Analysis (JDK8)
1. Inheritance of Classes
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
Analysis: HashSet inherits from the parent class (AbstractSet), implements the Set, Cloneable, java.io.Serializable interface, Serializable interface means HashSet implements serialization, that is, HashSet objects can be saved locally, and then restored.
Class relation diagrams:
Note: The green dotted line represents the implemented interface, and the yellow solid line represents the inherited class.
2. Class attributes
//serial number
static final long serialVersionUID = -5024744406713321676L;
// The underlying layer uses HashMap to save all elements in HashSet.
private transient HashMap<E,Object> map;
// Define a virtual Object object as the value of HashMap and define it as static final.
private transient HashMap<E,Object> map;
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
3. Class constructors
(1) Parametric-free constructor
public HashSet() {
//The default parametric constructor constructs an empty HashSet. The actual underlying layer initializes an empty HashMap, using the default initial capacity of 16 and the load factor of 0.75.
map = new HashMap<E,Object>();
}
(2) HashSet (Collection <? Extends E > c) type constructor
//Construct a new set containing elements in the specified collection, and the actual underlying layer creates a HashMap with a default load factor of 0.75 and an initial capacity sufficient to contain all elements in the specified collection.
public HashSet(Collection<? extends E> c) {
map = new HashMap<E,Object>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}
Note: The elements in c will be stored in collection in this set.
(3) HashSet(int float) type constructor
//An empty HashSet is constructed with the specified initial Capacity and loadFactor, and an empty HashMap is constructed with the corresponding parameters at the actual bottom.
public HashSet(int initialCapacity, float loadFactor) {
map = new HashMap<E,Object>(initialCapacity, loadFactor);
}
(4) HashSet (int) type constructor
//An empty HashSet is constructed with the specified initial Capacity, and an empty HashMap is constructed with the corresponding parameters and loading factor loadFactor of 0.75 at the actual bottom.
public HashSet(int initialCapacity) {
map = new HashMap<E,Object>(initialCapacity);
}
(5) HashSet(int,float,boolean) type constructor
//Construct a new empty link hash set (dummy tag) with the specified initial Capacity and loadFactor
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<E,Object>(initialCapacity, loadFactor);
}
Note: This constructor is package access permission and is not open to the public. In fact, it only supports LinkedHashSet. The actual underlying layer is implemented by constructing an empty LinkedHashMap instance with the specified parameters.
4. Class methods
(1) iterator method (returns an iterator that iterates over the elements in this set. The order in which elements are returned is not specific)
//The underlying layer actually calls the keySet of HashMap to return all keys.
public Iterator<E> iterator() {
return map.keySet().iterator();
}
Note: The source code shows that elements in HashSet are stored only on the key of the underlying HashMap, and value is identified by an Object object of static final. Iterator iterates over the elements in this set.
(2) The size method (returns the number of elements in this set)
//The bottom layer actually calls the size() method of HashMap to return the number of Entries, and gets the number of elements in the Set.
public int size() {
return map.size();
}
(3) isEmpty method (if this set does not contain any elements, it returns true)
//The underlying layer actually calls isEmpty() of HashMap to determine whether the HashSet is empty
public boolean isEmpty() {
return map.isEmpty();
}
(4) Contains method (returns true if the set contains the specified element)
//The bottom layer actually calls the containsKey of HashMap to determine whether the specified key (o is the element in this set that has been tested)
public boolean contains(Object o) {
return map.containsKey(o);
}
Note: When this set contains an e element that satisfies (o= null? E= null: O. equals (e)), return true.
(5) add method (if the set does not contain the specified element, add the specified element)
//The underlying layer will actually put this element in HashMap as a key.
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
Note: If this set does not contain the element E2 that satisfies (e= null? E2= null: E. equals (e2)), add the specified element E to this set. If the set already contains the element, the call does not change the set and returns false.
Because the put() method of HashMap adds key-value pairs, when key is newly placed in Entry of HashMap
The value of the newly added Entry will override the value of the original Entry, but the key will not change. Therefore, if an existing element is added to the HashSet, the newly added collection element will not be put into the HashMap and the original element will not have any. Change, which also satisfies the non-repetitive nature of the elements in the Set.
(6) remove method (if the specified element exists in this set, remove it)
//The underlying layer actually calls the remote method of HashMap to delete the specified Entry.
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}
Note: If this set contains an element E that satisfies (o= null? E= null: O. equals (e)), remove it. If this set already contains this element, it returns true, or if the set changes due to the call, it returns true. Once the call returns, the set no longer contains the element.
(7) clear method (remove all elements from this set)
//The bottom layer actually calls the clear method of HashMap to empty all elements in Entry.
public void clear() {
map.clear();
}
Note: When this call returns, the set will be empty
(8) clone method (returns a shallow copy of this HashSet instance)
//The underlying layer actually calls the clone() method of HashMap to get a superficial copy of HashMap and set it to HashSet.
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();
}
}
}
Note: The clone method does not copy the elements themselves.
5. Method summary in Java API
Method | Return value |
---|---|
boolean add(E e) | If the set does not contain the specified element, add the specified element. |
void clear() | Remove all elements from the set. |
Object clone() | Returns a superficial copy of this HashSet instance: The elements themselves are not copied. |
boolean contains(Object o) | If this set contains the specified element, return true. |
boolean isEmpty() | If this set does not contain any elements, it returns true. |
Iterator iterator() | Returns an iterator that iterates over the elements in this set. |
boolean remove(Object o) | If the specified element exists in this set, it is removed. |
int size() | Returns the number of elements (set capacity) in this set. |
If there are any mistakes, please point out.
Thank you!