版权声明:欢迎转载,转载请标明来源 https://blog.csdn.net/weixin_41973131/article/details/88951321
一 简介
HashSet可以存放null值,但不允许存在两个相等的元素,后面添加的相同元素会替换掉前者,并且null也只允许存在一个。
由于HashSet的迭代器消耗的时间与HashSet实例元素的大小成正比,再加上capacity容量是依靠HashMap进行实现的,因此在迭代器的效率需要得到重视的情况下,请不要设置太大的初始容量以及太小的加载因子
HashSet并没有实现同步,因此在多条线程操作HashSet并且存在对数据进行修改的情况下,需要在HashSet的外部进行同步处理。
HashSet是利用HashMap进行封装后的产物,定义一个final的Object类,并利用HashMap(key, Object object)以HashMap作为HashSet的基础结构。将增加元素设置为当key相同时不修改该键值对,从而保证了键key的唯一性。并且只针对HashMap中键key的操作构成了HashSet的简单API,并且HashSet的迭代器与可分割迭代器也是使用了HashMap的键迭代器以及键可分割迭代器。
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
{
}
二 成员变量
// HashMap实例
private transient HashMap<E,Object> map;
// 与Map中的对象关联的虚值
private static final Object PRESENT = new Object();
三 构造器
// 构建一个空的set
public HashSet() {
map = new HashMap<>();
}
// 使用指定集合构建一个新的Set,Map实例的加载因子为0.75,容量为16或更高
public HashSet(Collection<? extends E> c) {
map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}
// 利用指定的初始化容量以及加载因子构建一个新的HashMap
public HashSet(int initialCapacity, float loadFactor) {
map = new HashMap<>(initialCapacity, loadFactor);
}
// 构建一个指定容量的HashSet,加载因子默认为0.75
public HashSet(int initialCapacity) {
map = new HashMap<>(initialCapacity);
}
// 构建一个新的LinkedHashSet,这个构造器只能被LinkedHashMap使用,使用指定初始化容量和加载因子构造一个LinkedHashMap,dummy可忽略,只是为了区分于int, float构造器
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<>(initialCapacity, loadFactor);
}
四 构造器
// 返回该Set集合中元素的迭代器,而该构造器的返回元素是没有特定顺序的
public Iterator<E> iterator() {
return map.keySet().iterator();
}
五 基本API
// 返回set中元素的数量
public int size() {
return map.size();
}
// 如果集合中不存在元素则返回true
public boolean isEmpty() {
return map.isEmpty();
}
// 如果Set集合中存在指定元素则返回true
public boolean contains(Object o) {
return map.containsKey(o);
}
// 在Set中不存在指定值的前提下,将e存储到Set中
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
// 如果Set中包含指定值e,则在Set中移除掉该指定值
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}
// 移除Set中所有的元素
public void clear() {
map.clear();
}
六 克隆
// 返回一个HashSet的浅赋值
@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);
}
}
// 将HashSet实例的状态存储到流中(序列化)
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 {
// 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);
}
// 设置容量大小
capacity = (int) Math.min(size * Math.min(1 / loadFactor, 4.0f),
HashMap.MAXIMUM_CAPACITY);
// 当添加第一个元素时,构造后端映射将惰性地创建一个数组,需要在构造前检查它。利用HashMap的tableSizeof()来计算实际的分配大小。检查Map.Entry[].class,因为它是与实际创建内容最近的公共类型。
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);
}
}
七 可分割迭代器
// 创建一个可分割迭代器
public Spliterator<E> spliterator() {
return new HashMap.KeySpliterator<E,Object>(map, 0, -1, 0, 0);
}
八 最后
HashSet很大程度上是利用HashMap实现的,其实懂了HashMap的话,HashSet基本没啥理解难度。