HashSet
基于HashMap实现的散列集
目录
HashSet继承关系
HashSet实现了Serializable接口,支持序列化,可通过序列化传输
HashSet实现了Cloneable接口,覆盖了clone()方法,能被克隆
HashSet继承了AbstractSet抽象类和实现了Set接口,可以进行集的相关操作
HashSet源码分析
HashSet构造函数:
/**
* 构造一个空集
* 初始容量initailCapacity = 16
* 负载因子loadFacto = 0.75
*/
public HashSet() {
map = new HashMap<>(); // 底层实现使用HashMap存储数据
}
/**
* 构造一个包含指定集合中元素的新集
* 初始容量initailCapacity = 容纳指定集合中的元素
* 负载因子loadFactor = 0.75
*
* @param c the collection whose elements are to be placed into this set
* @throws NullPointerException if the specified collection is null
*/
public HashSet(Collection<? extends E> c) {
// 初始容量 = Max(指定集合元素数量/0.75 + 1,16), 使loadFactor <= 0.75
map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}
/**
* 构造一个 指定初始容量 和 指定负载因子 的新集
*
* @param initialCapacity the initial capacity of the hash map
* @param loadFactor the load factor of the hash map
* @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);
}
/**
* 构造一个 指定初始容量 和 默认负载因子=0.75 的新集
*
* @param initialCapacity the initial capacity of the hash table
* @throws IllegalArgumentException if the initial capacity is less
* than zero
*/
public HashSet(int initialCapacity) {
map = new HashMap<>(initialCapacity);
}
/**
* 构造一个 指定初始容量 和 指定加载因子 新集, 以LinkedHashMap为实现
* 此构造函数仅由LinkedHashSet使用
*
* @param initialCapacity the initial capacity of the hash map
* @param loadFactor the load factor of the hash map
* @param dummy ignored (distinguishes this
* constructor from other int, float constructor.)
* @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);
}
HashSet增删操作:
/**
* 若此元素集中不包含任何元素e2, 使Objects.equals(e, e2), 则将指定的元素e添加到该元素集中
* 若此集已包含该元素, 则调用将保留该集不变, 返回false
*
* @param e element to be added to this set
* @return {@code true} if this set did not already contain the specified
* element
*/
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
/**
* 若此元素集中包含任何元素o, 使Objects.equals(o, o2), 则删除元素o后返回true, 否则返回false
*
* @param o object to be removed from this set, if present
* @return {@code true} if the set contained the specified element
*/
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}
/**
* 从集中删除所有元素
*/
public void clear() {
map.clear();
}
HashSet其他操作:
/**
* 返回迭HashMap的key迭代器
*/
public Iterator<E> iterator() {
return map.keySet().iterator();
}
/**
* 返回此集中的元素数量
*
* @return the number of elements in this set (its cardinality)
*/
public int size() {
return map.size();
}
/**
* 若此集不包含任何元素, 则返回true
*
* @return {@code true} if this set contains no elements
*/
public boolean isEmpty() {
return map.isEmpty();
}
/**
* 当且仅当此集包含元素e使Objects.equals(o, e)时, 返回true
*
* @param o element whose presence in this set is to be tested
* @return {@code true} if this set contains the specified element
*/
public boolean contains(Object o) {
return map.containsKey(o);
}
HashSet序列化操作:
/**
* 将集保存到流中(发送实例容量和负载因子, 然后发出集的大小, 然后发出其所有元素)
*
* @serialData The capacity of the backing {@code HashMap} 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);
}
/**
* 从流中重构HashSet
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject(); // 从此流中读取当前类的非静态和非瞬态字段, 只能从readObject方法中调用, 否则抛出NotActiveException
// 读取容量并验证
int capacity = s.readInt();
if (capacity < 0) {
throw new InvalidObjectException("Illegal capacity: " +
capacity);
}
// 读取负载因子并验证
float loadFactor = s.readFloat();
if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
throw new InvalidObjectException("Illegal load factor: " +
loadFactor);
}
// 读取大小并验证
int size = s.readInt();
if (size < 0) {
throw new InvalidObjectException("Illegal size: " +
size);
}
// 根据大小和负载因子设置容量, 确保HashMap至少已满25%, 但要限制小于最大容量
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.getJavaObjectInputStreamAccess()
.checkArray(s, Map.Entry[].class, HashMap.tableSizeFor(capacity));
// Create backing HashMap
map = (((HashSet<?>)this) instanceof LinkedHashSet ?
new LinkedHashMap<>(capacity, loadFactor) :
new HashMap<>(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);
}
}
HashSet总结
HashSet底层通过维护一个HashMap实现
HashSet初始的默认容量=16,默认负载因子=0.75
HashSet数据结构保证了数据的唯一性,但不保证数据的有序性(唯一,无序)
HashSet是线程不安全的