HashSet实现了Set接口,也就是说它存储的元素是无重复的。
通过源码分析我们可以发现HashSet就是HashMap的一个实例。
因为在HashMap中的键是不能重复的,我们可以把HashSet想象成HashMap中的键,而且事实也就是如此。
public class HashSet
extends AbstractSet
implements Set
, Cloneable, java.io.Serializable
{
static final long serialVersionUID = -5024744406713321676L;
//底层使用HashMap来保存HashSet的元素
private transient HashMap
map;
// 虚拟的对象,用来支持HashMap
private static final Object PRESENT = new Object();
/**
* 创建一个新的空的HashSet,用来支持HashMap,默认初始容量和HashMap的一样
* 初始容量16,加载因子0.75
*/
public HashSet() {
map = new HashMap
(); } /** * 将一个set集合存入HashSet中, * 类似HashMap中的将一个子集放入已有的HashMap内 * 底层调用HashMap实现 */ public HashSet(Collection
c) { map = new HashMap
(Math.max((int) (c.size()/.75f) + 1, 16)); addAll(c); } /** * 构造一个空的HashSet,产生的是一个HashMap实例, * 其值便是成员变量中的Object,并且指定容量和加载因子 * */ public HashSet(int initialCapacity, float loadFactor) { map = new HashMap
(initialCapacity, loadFactor); } /** * 构造一个空的HashSet,指定容量,加载因子为默认的 * 如果看过HashMap的话,就会知道这个在HashMap中会默认调用上面那个构造函数 */ public HashSet(int initialCapacity) { map = new HashMap
(initialCapacity); } /** * 此构造方法是默认的,只能被LinkHashSet访问,表示构造一个空的LinkHashSet, * 我们可以看出来,这也是以HashMap作为基础。 */ HashSet(int initialCapacity, float loadFactor, boolean dummy) { map = new LinkedHashMap
(initialCapacity, loadFactor); } /** * 返回这个set的迭代器,可以看出这个底层是调用了HashMap的 * keySet方法返回所有的Key */ public Iterator
iterator() { return map.keySet().iterator(); } /** * 返回HashSet的实际大小,也就是一共有多少个键 */ public int size() { return map.size(); } /** * 当HashSet为空时返回true */ public boolean isEmpty() { return map.isEmpty(); } /** * 如果Set中包含了指定的元素,返回true. * 更确切的说是:当且仅当此 set 包含一个满足 * (o==null ? e==null : o.equals(e)) 的 e 元素时,返回 true。 * */ public boolean contains(Object o) { return map.containsKey(o); } /** * 往HashSet中添加一个值,可以发现当添加的值已经存在时, * 也就是两者的key的hashcode值相等并且key通过equals比较也相等, * 用新的value覆盖旧的value,但是key的值不会有什么变化 */ public boolean add(E e) { return map.put(e, PRESENT)==null; } /** * 移除的对象和以前放入的对象一致,则移除成功 */ public boolean remove(Object o) { return map.remove(o)==PRESENT; } /** * 清空HashSet */ public void clear() { map.clear(); } /** * 返回HashSet的一个浅拷贝,调用的是HashMap的浅拷贝, * 因为只拷贝的是里面对象的引用,具体的引用没有拷贝 */ public Object clone() { try { HashSet
newSet = (HashSet
) super.clone(); newSet.map = (HashMap
) map.clone(); return newSet; } catch (CloneNotSupportedException e) { throw new InternalError(); } } /** *将map的容量、加载因子、大小以及每个值都序列化 */ 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()); } /** *反序列化时如果类型是LinkedHashSet则用LinkedHashMap进行处理 * 否则用HashMap进行处理 */ private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { // Read in any hidden serialization magic s.defaultReadObject(); // Read in HashMap capacity and load factor and create backing HashMap int capacity = s.readInt(); float loadFactor = s.readFloat(); map = (((HashSet)this) instanceof LinkedHashSet ? new LinkedHashMap
(capacity, loadFactor) : new HashMap
(capacity, loadFactor)); // Read in size int size = s.readInt(); // Read in all elements in the proper order. for (int i=0; i
接下来我们具体分析一个构造函数:
public HashSet(Collection
c) {
map = new HashMap
(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}
//注意到这个addAll方法,深入进去后,发现是AbstractCollection类中的
/**
*这里的这个迭代器就是HashSet中我们定义的那个迭代器
*所以就是遍历参数中的set,添加到我们原有的set中
*/
public boolean addAll(Collection
c) {
boolean modified = false;
Iterator
e = c.iterator();
while (e.hasNext()) {
if (add(e.next()))
modified = true;
}
return modified;
}
public Iterator
iterator() {
return map.keySet().iterator();
}
/**
* 我们注意到add中的代码是跟null进行比较
* 这是因为我们在HashMap中添加元素时,如果key不存在就返回null,否则就返回旧值
*/
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}