Java基础之Collections框架Set实现类HashSet及其源码分析
这个类实现了Set接口,由一个哈希表(实际上是一个HashMap实例,HashMap研究到了再说,哈哈)支持。它不能保证集合的迭代顺序;特别是,它不能保证顺序随时间保持不变。这个类允许空元素。
这个类为基本操作(添加、删除、包含和大小)提供恒定的时间性能,假设哈希函数将元素正确地分散在bucket中。遍历这个集合所需的时间与HashSet实例的大小(元素的数量)与支持HashMap实例的“容量”(桶的数量)的总和成正比。因此,如果迭代性能很重要,那么不要将初始容量设置得太高(或负载系数设置得太低),这一点非常重要。
HashSet简单使用
Set<String> example = new Hash<>();
//添加元素
example.add("tony");
example.add("project");
//移除元素
example.remove("tony");
//包含
example.contains("project");
//判断为空
example.isEmpty();
还有很多方法,我们来看看HastSet的源码
HashSet源码分析
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
{
//序列版本编号
static final long serialVersionUID = -5024744406713321676L;
//HashMap对象
private transient HashMap<E,Object> map;
// 伪值,以便与后备映射中的对象相关联
private static final Object PRESENT = new Object();
/**
HashSet的默认构造函数,创建一个HashMap,默认大小为16,扩容因子为0.75
*/
public HashSet() {
map = new HashMap<>();
}
/**
* 构造一个包含指定元素的新集合,默认大小为16,扩容因子为0.75
* 如果集合的大小为12的时候,初始化的大小为17,进行了一次扩容,如果小于12的话,会返回16
*/
public HashSet(Collection<? extends E> c) {
//当c.size为12的时候,Math.max(17,16) 返回17 max((int)(12 /0.75) + 1,16)
map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}
/**
创建一个hashSet,指定初始容量和扩容因子
*/
public HashSet(int initialCapacity, float loadFactor) {
map = new HashMap<>(initialCapacity, loadFactor);
}
/**
创建一个hastSet,指定初始容量,使用默认的扩容因子 0.75
*/
public HashSet(int initialCapacity) {
map = new HashMap<>(initialCapacity);
}
/**
* 创建一个新的,空的 LinkedHashSet ,后台HashMap实例是一个LinkedHashMap,具有指定的初始容量和指定的扩容因子。
*/
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<>(initialCapacity, loadFactor);
}
/**
返回相应当前结合的迭代器,是使用HashMap中的,可以看看hashMap的相关源码
*/
public Iterator<E> iterator() {
return map.keySet().iterator();
}
/**
返回hashSet集合中的元素个数
*/
public int size() {
return map.size();
}
/**
返回set集合是否为空
*/
public boolean isEmpty() {
return map.isEmpty();
}
/**
返回set集合中是否包含指定的元素
*/
public boolean contains(Object o) {
return map.containsKey(o);
}
/**
* 如果指定的元素不存在,则将其添加到此集合中
* 如果存在将返回false
*/
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
/**
* 如果指定元素存在,则从该集合中移除该元素。
*/
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}
/**
移除结合中的所有的元素
*/
public void clear() {
map.clear();
}
/**
返回一个浅复制的HashSet
*/
@SuppressWarnings("unchecked")
public Object clone() {
try {
//这里直接copy的是hashMap中的值
HashSet<E> newSet = (HashSet<E>) super.clone();
newSet.map = (HashMap<E, Object>) map.clone();
return newSet;
} catch (CloneNotSupportedException e) {
throw new InternalError(e);
}
}
/**
保存HashSet的状态到相关的流中 序列化
序列化数据:HashMap实例的容量大小和扩容因子
后面是集合的大小(包含的元素数量) (int),后面是集合的所有元素(每个都是对象),没有特定的顺序。
*/
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException {
// 写出任何隐藏的序列化魔法
s.defaultWriteObject();
// 写出HashMap 容量和扩充因子
s.writeInt(map.capacity());
s.writeFloat(map.loadFactor());
// 写出大小
s.writeInt(map.size());
// 以适当的顺序写出所有元素
for (E e : map.keySet())
s.writeObject(e);
}
/**
* 反序列化出HashSet实例
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
// 读取任何隐藏的序列化魔法
s.defaultReadObject();
// 读取容量并验证非负性
int capacity = s.readInt();
if (capacity < 0) {
throw new InvalidObjectException("Illegal capacity: " +
capacity);
}
// 读取负载因数并验证正和非NaN
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);
}
// 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);
// 创建一个HashMap或者LinkedHashMap
map = (((HashSet<?>)this) instanceof LinkedHashSet ?
new LinkedHashMap<E,Object>(capacity, loadFactor) :
new HashMap<E,Object>(capacity, loadFactor));
//读取所有的元素
for (int i=0; i<size; i++) {
@SuppressWarnings("unchecked")
E e = (E) s.readObject();
map.put(e, PRESENT);
}
}
/**
创建一个Spliterator
*/
public Spliterator<E> spliterator() {
return new HashMap.KeySpliterator<E,Object>(map, 0, -1, 0, 0);
}
}
从上面的源码可以看出,操作HashSet就是操作HashMap中的key,key对应的都是一个空的object对象。Object PRESENT = new Object(); 因为HashMap中的key是不能重复,是不是就可以作为HashSet使用呢!