关于hashmap的几个核心知识点
1.HashMap原理,内部数据结构
一图胜千言
HashMap底层使用哈希表(数组+链表),当链表过长会将链表转换成红黑树以实现O(logn)时间复杂度内查找.
通过源码验证一下
2.HashMap中put方法过程
第一步:对Key求Hash值,然后计算下标
第二步:如果产生碰撞,以链表的方式链接到后面
第三步:如果链表长度超过阈值(TREEIFY_THRESHOLD == 8),就转成红黑树
第四步:如果节点已经存在就替换
第五步:如果桶满了(容量 * 加载因子),就需要进行resize扩容
3.HashMap中hash函数是怎么实现的?还有哪些hash的实现方式
直接看源码
这行代码的意思是:对key的hashCode值h右移16位再进行异或.举个例子:
| 假如Key的hashCode | 000000001100 000000000011 |
| 右移16位 | 000000000000 000000001100 |
| 进行异或(相同0,不同1) | 000000001100 000000001111 |
可以看到高16bit不变,高16位低16bit做了一个异或,这样做的好处是:
1.异或相比与,或更分散.
2.混合原始哈希码的高位和低位,以此来加大低位的随机性。而且混合后的低位掺杂了高位的部分特征,这样高位的信息也被变相保留下来。
3.3还有哪些实现方式
开放寻址法
当关键字key的哈希地址p =H(key)出现冲突时,以p为基础,产生另一个哈希地址p1,若p1仍然冲突,再以p为基础,产生另一个哈希地址p2,…,直到找出一个不冲突的哈希地址pi ,将相应元素存入其中。
再哈希法
就是当通过某个hash函数计算的key存在冲突时,再用另外一个hash函数对这个key做hash,一直运算直到不再产生冲突。这种方式会增加计算时间,性能影响较大。
公共溢出区
4.讲一下扩容过程
HashMap的扩展原理是HashMap用一个新的数组替换原来的数组。重新计算原数组的所有数据并插入一个新数组,然后指向新数组。如果阵列在容量扩展前已达到最大值,阈值将直接设置为最大整数返回。
扩容的时机:
添加完一个数据后,比较当前size和容量,如果大于就执行resize()
手写hashMap的实现
初始容量是怎么计算方法:
static final int tableSizeFor(int cap) {
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
最后得出的结果一定是2的N次幂,
原理就是看最高位在哪里,最后加1变成00010000
为什么要这样做呢,容量为什么建议是2的幂次方?有两个原因
1.源码中官方解释大概意思,以二次幂展开,容器的元素要么保持原来的索引,要么以二次幂的偏移量出现在新表中.
这样可以尽可能的减少元素位置的移动
2.可以使元素均匀的散布hashmap中,减少哈希碰撞,因为hashmap确定元素小标是根据(n-1) & hash
因为n为2的幂次方n-1的二进制数据的低位就全部为1了,比如当数组长度为16,那么15的二进制就为1111,这样不仅效率高在计算数组下标index的时候才能更好地利用h的散列性
put方法的过程
Put的时候利用 hash值 & (n-1) 保证下标在索引范围内,举个例子
| 假如hashCode | 000000001100 000000001111 |
| &上n-1(&都为1才为1) | 000000000000 000000001100 |
| 结果 | 000000001100 000000001111 |
相同节点,JDK8之前利用头插法插入到链表中,JDK8之后采用尾插法.为什么这么改呢
JDK1.7用的是头插法,而JDK1.8及之后使用的都是尾插法,那么他们为什么要这样做呢?因为JDK1.7是用单链表进行的纵向延伸,当采用头插法时会容易出现逆序且环形链表死循环问题。但是在JDK1.8之后是因为加入了红黑树使用尾插法,能够避免出现逆序且链表死循环的问题。
但是为什么尾插法会避免死循环? blog.csdn.net/qq_45915957…
Hashmap为什么不是线程安全的
1.put的时候会造成覆盖
2.Resize 的时候,会造成循环链表的问题
红黑树
如果链表长度超过阈值(TREEIFY_THRESHOLD == 8),就转成红黑树,关于红黑树的知识,先粗浅的看一下它长什么样子,这部分内容就放在数据结构与算法那部分章节再分享吧.
参考链接:
blog.csdn.net/liuxingrong… blog.csdn.net/fengxi_tan/… blog.csdn.net/weixin_4436…