一、HashSet介绍
1、HashSet底层是HashMap(数组+链表+红黑树)
2、HashSet这个类实现了Set集合,实际为一个HashMap的实例。对集合的迭代次序没有任何保证; 特别是,它不能保证订单会随着时间的推移保持不变。这个类允许null 元素。
3、有三个构造函数:
HashSet(Collection<? extends E> c)
构造一个包含指定集合中的元素的新集合。
HashSet(int initialCapacity)
构造一个新的空集合; 背景HashMap实例具有指定的初始容量和默认负载因子(0.75)。
HashSet(int initialCapacity, float loadFactor)
构造一个新的空集合; 背景HashMap实例具有指定的初始容量和指定的负载因子。
二、HashSet执行机制
1、添加一个元素时,先得到hash值,然后通过特定算法转成索引值
2、找到存储数据表table,看这个索引位置是否已经存放有元素
3、如果没有,直接加入
4、如果有,调用equals比较,如果相同,则放弃添加,如果不同则添加到最后
5、在JAVA8中,如果一条链表的元素个数超过8,table的大小>=64就会进行红黑树化
三、源码解读
/*
1、执行HashSet()
public HashSet() {
map = new HashMap<>();
}
2、执行 add()
public boolean add(E e) {
return map.put(e, PRESENT)==null;//PRESENT =private static final Object PRESENT = new Object();
}
3、执行 put(),该方法会执行 hash(key)得到key对应的hash值
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
4、执行 putVal
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;//定义辅助变量
//table就是HashMap的一个数组,类型就是Node[]
//if语句表示如果当前table 是null,或者 大小=0
/就是第一次扩容,到16个空间
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
//(1)根据key,得到hash去计算该key应该存放到table表的哪个索引位置
//并且把这个位置的对象赋给 P
//(2)判断p是否为null
//(2.1)如果p 为null,表示还没有存放元素,就创建一个Node
//(2.2)就放在该位置tab[i] = newNode(hash, key, value, null);
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;//局部辅助变量
//如果当前索引位置对应的链表的第一个元素和准备添加的key的hash值一样
//并且满足 下面两个条件之一:
//(1)准备加入的key和 p指向的Node节点的key是同一个对象
//(2)p指向的Node结点的key的equals()和准备加入的key比较后相同
//就不能加入
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
//再判断 p 是不是一颗红黑树
//如果是一颗红黑树,就调用putTreeVal进行添加
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
//如果table对应索引位置已经是一个链表,就使用for循环进行比较
//(1) 依次和该链表的每一个元素进行比较后,如果都不相同,就加入到该链表的最后
// 注意在把元素添加到链表后应该立即进行判断该链表是否达到8个结点
// 如果达到,则调用treeifyBin()对当前链表进行红黑树化
// 在转化为红黑树的时候要进行判断,判断条件
// if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
// resize();
// 条件成立先table扩容
// 当条件不成立的时候才进行转化为红黑树
//(2)如果有相同的情况,就直接break
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;//到达最后添加后退出
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;//有相同的所以退出
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
//在这里真正实现扩容
//size 是我们每加入一个结点Node(k,v,h,next)
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
*/