HashMap
- HashMap的数据结构:HashMap实际上是一个数组和链表(“链表散列”)的数据结构。底层就是一个数组结构,数组中的每一项又是一个链表。
- hashCode是一个对象的标识,Java中对象的hashCode是一个int类型值。通过hashCode来算出指定数组的索引可以快速定位到要找的对象在数组中的位置,之后再遍历链表找到对应值,理想情况下时间复杂度为O(1),并且不同对象可以拥有相同的hashCode(hash碰撞)。发生碰撞后会把相同hashcode的对象放到同一个链表里,但是在数组大小不变的情况下,存放键值对越多,查找的时间效率也会降低
- 扩容可以解决该问题,而负载因子决定了什么时候扩容,负载因子是已存键值对的数量和总的数组长度的比值。默认情况下负载因子为0.75,我们可在初始化HashMap的时候自己修改。阀值 = 当前数组长度✖负载因子
- hashmap中默认负载因子为0.75,长度默认是16,默认情况下第一次扩容判断阀值是16 ✖ 0.75 = 12;所以第一次存键值对的时候,在存到第13个键值对时就需要扩容了,变成16X2=32。
put流程
- 对key hash,二次hash,hash扰乱函数,减少hash碰撞
int hash(Object key) {
int h = key.hashCode();
return (h ^ (h >>> 16)) & (capitity -1); //capicity表示散列表的大小
}
获取对象的hashcode以后,先进行移位运算,然后再和自己做异或运算,即:hashcode ^ (hashcode >>> 16),这一步甚是巧妙,是将高16位移到低16位,这样计算出来的整型值将“具有”高位和低位的性质
- 通过hash算出数组角标(indexfor())
- 添加元素,看是否需要扩容,需要的话变数组变成原来的2倍,把旧的拷贝到新的数组上去,然后旧的指针指向新的。
- 如果key相同的覆盖;没有的话添加元素
get流程
- get对key hash,找到数组角标(indexfor())
- 如果hash相同key相同就找到了
- 如果hash相同key不相同,找链表的下一个(通过值找)
其他问题
- 1.7 和 1.8 数据结构有什么不同?
1.8 增加了转换为红⿊树 - 插⼊数据的⽅式
1.7 的链表从前⾯插⼊,1.8 的链表从后⾯插⼊ - HashMap 什么时候会把链表转化为红⿊树?
链表⻓度超过 8 ,并且数组⻓度不⼩于 64
在 JDK1.8 版本中,为了对 HashMap 做进一步优化,引入了红黑树。而当链表长度太长(默认超过 8)时,链表就转换为红黑树。可以利用红黑树快速增删改查的特点,提高 HashMap 的性能。当红黑树结点个数少于 8 个的时候,又会将红黑树转化为链表。因为在数据量较小的情况下,