集合浅谈之HashSet篇
简介
- HashSet允许存在null值,不允许set中元素重复,也就是只允许存在一个null值
- HashSet源码中是通过HashMap实现的,与键进行关联的值为一个虚拟值,值没有什么意义故其元素存放规则同HashMap相同,是按照hash值进行存放故其中的元素是无序的
源码分析
public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable
{
//HashSet中存在一个HashMap引用类型对象,键为需要存储到HashSet中的值,值为没有实际意义的Object对象,其实HashSet的本质还是HashMap
private transient HashMap<E,Object> map;
//与键进行关联的虚拟值,每个键的值都为PRESENT
private static final Object PRESENT = new Object();
/**
构造方法
通过构造方法我们可以看到调用HashMap中的构造方法,构造的HashMap对象
*/
//默认无参构造方法
public HashSet() {
map = new HashMap<>();
}
//通过另一个集合构造HashSet
public HashSet(Collection<? extends E> c) {
//构造HashMap对象,初始容量使用HashMap的默认容量与集合容量/构造因子得到容量中的较大值
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);
}
//hashMap中指定初始容量的构造方法
public HashSet(int initialCapacity) {
map = new HashMap<>(initialCapacity);
}
//用于构造一个LinkedHashMap,该构造方法只能由LinkedHashSet使用
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<>(initialCapacity, loadFactor);
}
//add()方法,add方法最终调用了HashMap中的putVal()方法,键值对中值为虚拟值PRESENT
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
}
问1:HashSet是如何做到其中元素不重复的?
如前文所述,HashMap把需要存储的值放置于HashMap的key位置,HashMap的key是不能重复的(至于HashMap中如何保证key的不重复,在HashMap篇中有说到),故HashSet中不允许重复元素
问2:HashSet中为什么没有得到元素的方法(get系列)?
- HashSet是根据key的hash值去存储元素,因此无法根据索引去访问元素
- HashSet不同于HashMap可以通过键去访问值,HashSet直接把值存储在键中,因此无法通过键去得到值
- HashSet只能通过迭代器去访问其中的元素