【HashMap】知识点归纳、HashMap面试题总结
此篇文章为HashMap常见知识点归纳及面试题总结。
想了解从源码过程分析,可以看我的另一篇文章:↓ 一步步从源码分析,通俗易懂!
JDK1.7: 【HashMap-1.7】-从源码解析-通俗易懂【扩容】【线程不安全】【put&get原理】【遍历删除】.
总结
针对Jdk7
HashMap的默认长度是多少?
调用无参构造,默认长度为16
HashMap为什么Key可为空?
put方法校验当key为null 时,会将元素存储到 table[0] 的位置上
HashMap的数据结构与底层原理?
1.7 采用数组 + 链表
当 key 冲突时 ,会形成链表
【拓展】关于1.8 后续会出文章详解
1.8 采用 数组 + 链表 + 红黑树
当碰撞时 形成链表,当链表长度大于TREEIFY THRESHOLD会形成红黑树
HashMap计算索引为什么是h & (length-1) ?
由于底层存放的是entry数组结构,计算索引时,要使得 取值范围刚好在 0 ~ 数组长度-1 的范围内。
第一想法可能是想到 用 取模 运算 h % length ? 不是相同的结果么?
不用取模运算 是因为 取模运算是 是 进行一步一步的除法,得到最终的余数。
计算机在二进制位运算 时的效率 要远大于 取模运算的。
HashMap的长度为什么要取2的次幂?
如果不采取2的次幂值,当进行put时,计算索引 " h & (length-1) " 时,转换为二进制位运算 最后一位 为0 ,
会大大增加不同hash值 计算索引值相同的几率,从而倒置碰撞的几率增大。
例如 key ="LL" hahs="2315" 按位与计算 转化为二进制
当前 length = 15 的话
h & (length-1) = h & 14
h 1001 0000 1011
0000 0000 1110
= 0000 0000 1010
= 10
假如 hash="2314"
h & (length-1) = h & 14
h 1001 0000 1010
0000 0000 1110
= 0000 0000 1010
= 10
不同的h值计算的索引一致
HashMap是线程安全的么?为什么?
不是线程安全的。
多线程在扩容时容易产生循环链表情况,当下次get到当前位置上时,会造成死循环!
原因是:
由于在hashMap 在扩容时 会将原数组上的值 转移到 扩容后的数组上,采用头插法。
会产生链表元素倒置情况。
若两个线程都进行扩容,进行完 Entry<K,V> next = e.next;该操作时,线程2挂起。
线程1执行完transfer方法后,线程2继续执行,由于已经存储了 数组开始时的引用,
在转移元素时会产生循环链表情况。【具体过程 可看上面扩容过程】
在调用get方法时,key匹配到产生循环链表上时,会产生死循环。
线程安全的Map有哪些?
我们会想到
HashTable
是因为 hashTable 在 put、get、size、remove 等等方法上 加上了synchronized 简单粗暴 保证了线程同步。
因为是 直接 synchronized 加在 方法上 效率受很大影响
ConcurrentHashMap
底层采用分段的数组 + 链表实现 只锁住当前段,效率较高
HashMap为什么在1.8中引用了红黑树?
1.7 采用数组 + 链表
当 key 冲突时 ,会形成链表
【拓展】关于1.8 后续会出文章详解
1.8 采用 数组 + 链表 + 红黑树
当碰撞时 形成链表,当链表长度大于TREEIFY THRESHOLD会转化为红黑树
因为:
如果采用数组 + 链表的形式,当元素达到一定量的时候,并且key冲突的元素过多时,会导致链表过长。
我们都知道,链表结构的缺点 遍历查找时 大大降低了效率。
以上内容,若有不足或错误,还望指正。
不止于前 未来可期 ···