HashSet(Set接口实现类)
HasSet说明
HasSet的底层其实调用了HasMap
1 | public HashSet() { |
Hashset可以存放null,只能存放一个
HashSet不保证元素是有序的,取决与hash后,在确定索引的结果
练习1
简单
1 | import java.util.*; |
进阶
1 | import java.util.*; |
HashSet的底层机制
HashSet的底层机制是HashMap,HashMap的底层是(数组+链表+红黑树)
数组+链表:提高存储的效率
而红黑树则是数组的存储到达64时,并且链表存储到达8时,形成层次性更强的树
HasSet的扩容机制
- 添加一个元素的时候,会先得到hash值,根据此值转成索引值
- 找到存放数据的表,看这个索引值对应的位置是否已经存放元素
- 如果没有则直接加入,如果有则调用equals比较,如果相同,就放弃添加(这也就解释了为什么不能添加相同的元素的原因),如果不相同,则添加到最后
- 在java8中如果一条链表个数超过8,并且table的大小>=64,会变成红黑树
1 | import java.util.*; |
在debug的模式下尝试第一次add,首先进入add方法
调用put方法,value是一个占位符,用于满足hashmap的方法
而这里的算法得到的值是将key.hashcode右移16位
而我们第一刺进putVal方法时,此时的table[]为空,调用resize方法进行扩容为16位,同时该方法也会检测table数组的使用是否达到了12(16*加载因子(0.75))位,如果达到,就再一次进行扩容达到32,而检测是树也达到了24
而当我们进行第二次扩容,经过内置的算法计算出lili应该放在8号索引位置,步骤与上述的相同
但是当我们进行第三次扩容时,由于加入相同的字符串对象,算法算出的hash值相等,得出同样都在3的索引位置,然后进行equals的比较,比较他们的值是否相同。
总结
针对上面的练习的进阶,我们运行以下代码
1 | import java.util.*; |
所以说hashset能否允许新元素的加入,key.hashCode()决定了它在数组的索引位置,而最终的比较还需要看equals方法的定义,当然在比较的过程中系统调用了equals方法,程序员也可以自己定义重写equals方法,改变比较的规则,比较hashcode是系统默认给出的方法
1 | import java.util.*; |
练习1
1 | import java.util.HashSet; |
练习2
1 | import java.time.LocalDate; |
练习3
1 | import java.util.*; |