**HashSet源码解析 **
蓝色线条:继承 绿色线条:接口实现
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
{
HaspSet 继承 HashSet 实现 Cloneable Set Serializable
(Cloneable 表明它支持克隆,可以调用超类的clone()方法进行浅拷贝)
HashSet 是一个 没有重复元素的集合
HashSet 由 HashMap 实现的 ,底层是通过 HashMap 存储元素
不保证 元素的顺序 , 可以存储null值 数据不能重复 数据不能保证插入有序
HashSet 是 非同步的 线程不安全的。
HashSet 通过 iterator() 返回的 迭代器 是 fail-fast 的。
···
如果多个线程同时访问一个哈希 set,而其中至少一个线程修改了该 set,
那么它必须 保持外部同步。
这通常是通过对自然封装该 set 的对象执行同步操作来完成的。
如果不存在这样的对象,则应该使用 Collections.synchronizedSet 方法来“包装” set。最好在创建时完成这一操作,以防止对该 set 进行意外的不同步访问
Set s = Collections.synchronizedSet(new HashSet(...));
···
底层源码
package java.util;
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
{
static final long serialVersionUID = -5024744406713321676L;
// HashSet是通过map(HashMap对象)保存内容的
private transient HashMap<E,Object> map;
// 定义一个虚拟的Object PRESENT是向map中插入key-value对应的value
// 因为HashSet中只需要用到key,而HashMap是key-value键值对;
// 所以,向map中添加键值对时,键值对的值固定是PRESENT
private static final Object PRESENT = new Object();
// 默认构造函数 底层创建一个HashMap
public HashSet() {
// 调用HashMap的默认构造函数,创建map
map = new HashMap<E,Object>();
}
// 带集合的构造函数
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)中的全部元素添加到HashSet中
addAll(c);
}
// 指定HashSet初始容量和加载因子的构造函数
public HashSet(int initialCapacity, float loadFactor) {
map = new HashMap<E,Object>(initialCapacity, loadFactor);
}
// 指定HashSet初始容量的构造函数
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的迭代器
public Iterator<E> iterator() {
// 实际上返回的是HashMap的“key集合的迭代器”
return map.keySet().iterator();
}
//调用HashMap的size()方法返回Entry的数量,得到该Set里元素的个数
public int size() {
return map.size();
}
//调用HashMap的isEmpty()来判断HaspSet是否为空
//HashMap为null。对应的HashSet也为空
public boolean isEmpty() {
return map.isEmpty();
}
//调用HashMap的containsKey判断是否包含指定的key
//HashSet的所有元素就是通过HashMap的key来保存的
public boolean contains(Object o) {
return map.containsKey(o);
}
// 将元素(e)添加到HashSet中,也就是将元素作为Key放入HashMap中
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
// 删除HashSet中的元素(o),其实是在HashMap中删除了以o为key的Entry
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}
//清空HashMap的clear方法清空所有Entry
public void clear() {
map.clear();
}
// 克隆一个HashSet,并返回Object对象
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();
}
}
// java.io.Serializable的写入函数
// 将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 (Iterator i=map.keySet().iterator(); i.hasNext(); )
s.writeObject(i.next());
}
// java.io.Serializable的读取函数
// 将HashSet的“总的容量,加载因子,实际容量,所有的元素”依次读出
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
// Read in any hidden serialization magic
s.defaultReadObject();
//读入HashMap容量和加载因子,并创建备份HashMap
int capacity = s.readInt();
float loadFactor = s.readFloat();
map = (((HashSet)this) instanceof LinkedHashSet ?
new LinkedHashMap<E,Object>(capacity, loadFactor) :
new HashMap<E,Object>(capacity, loadFactor));
// Read in size
int size = s.readInt();
// Read in all elements in the proper order.
for (int i=0; i<size; i++) {
E e = (E) s.readObject();
map.put(e, PRESENT);
}
}
}
HashSet 其实就是一个对HashMap的封装。
所有放入HashSet中的 集合元素 实际上由HashMap的 key 来保存,
而HashMap的 value 则存储了一个 PRESENT,它是一个静态的Object对象。
HashSet的绝大部分方法都是通过调用HashMap的方法来实现的,
因此HashSet和HashMap两个集合在实现本质上是相同的。
(根据HashMap的一个特性:
将一个key-value对放入HashMap中时,首先根据key的hashCode()返回值决定该Entry的存储位置,
如果两个key的hash值相同,那么它们的存储位置相同。
如果这个两个key的equalus比较返回true。
那么新添加的Entry的value会覆盖原来的Entry的value,key不会覆盖。)
因此,如果向HashSet中添加一个已经存在的元素,
新添加的集合元素不会覆盖原来已有的集合元素。
HashSet判断两个对象相等的标准是想通过hashCode()方法计算出其hash值,当hash值相同的时候才继续判断equals()方法。