Java版本:8u321。
HashSet的底层是通过HashMap来实现的,之前我写过对HashMap源码进行分析的文章,感兴趣的话可以查看《较真儿学源码系列-HashMap(逐行源码带你分析作者思路)》。
1 简介
相比于List接口实现,Set是无序不重复的集合,所以我们可以通过它来实现去重的业务逻辑。而具体到HashSet来说其底层是通过HashMap来实现的,也就是说HashSet的特性是通过HashMap来保证的(相同值会进行覆盖)。key为需要存储的值,而value是一个没有任何意义的占位值。所以在HashSet中,所有key对应的value都为这个空的占位置。我们不需要关注value,只需要关注key就行了。
但是相比于HashMap,HashSet不提供get方法,这是因为通过key找到的value总是那个虚拟的占位值,没有什么意义;而且对于Set来说插入的顺序并不等于实际存储的顺序(HashMap的散列特性),所以通过索引位找到值的get方法也是不能提供的。
2 构造器
/**
* HashSet:
* 无参构造器
* 可以看到底层就是调用的HashMap的无参构造器
*/
public HashSet() {
map = new HashMap<>();
}
/**
* 有参构造器
* 可以看到底层就是调用的HashMap的有参构造器
*/
public HashSet(int initialCapacity) {
map = new HashMap<>(initialCapacity);
}
/**
* 将其他集合转成HashSet
*/
public HashSet(Collection<? extends E> c) {
//因为HashMap有负载因子的概念,所以取原有集合的长度/0.75+1和16的最大值最为HashMap的初始容量
map = new HashMap<>(Math.max((int) (c.size() / .75f) + 1, 16));
//然后将原有集合的数据添加到HashSet中
addAll(c);
}
/**
* AbstractCollection:
* 第25行代码处:
* 可以看到是通过遍历调用add方法来实现的
*/
public boolean addAll(Collection<? extends E> c) {
boolean modified = false;
for (E e : c)
if (add(e))
modified = true;
return modified;
}
3 add方法
/**
* HashSet:
*/
public boolean add(E e) {
/*
这里直接调用的HashMap的put方法来存入值,PRESENT的定义为“private static final Object PRESENT = new Object();”,
其只是一个占位的虚拟节点,没有其他意义。而对于HashMap的put方法来说,返回值为null代表新插入了一个键值对,而返回值不为null
代表此时已经有这个键值对了,只是在做值覆盖的操作。所以通过判断HashMap插入结果是否为null,以此来说明HashSet是否插入成功了
*/
return map.put(e, PRESENT) == null;
}
4 remove方法
/**
* HashSet:
*/
public boolean remove(Object o) {
//通过调用HashMap的remove方法来实现的,如果返回值是PRESENT,代表之前有值;否则代表没值
return map.remove(o) == PRESENT;
}
5 clear方法
/**
* HashSet:
*/
public void clear() {
//依然是通过调用HashMap的clear方法来实现的
map.clear();
}
原创不易,未得准许,请勿转载,翻版必究