HashMap相关面试题
-
- HashMap的实现原理
- HashMap中数组和链表的作用
- hash冲突的解决方法
- 可以用Linkedlist去替代数组吗?
- HashMap的扩容条件
- 为什么扩容是2的次幂
- HashMap的put元素过程
- HashMap的get元素过程
- 你知道哪些hash算法
- 说说String的hashcode的实现
- JDK1.8对HashMap的改进
- 为什么在解决hash冲突的时候,不直接用红黑树?而选择先用链表,再转红黑树?
- 可以使用二叉查找树来替换红黑树吗?
- 当链表转为红黑树后,什么时候退化为链表?
- HashMap在并发编程环境下有什么问题?
- 如何解决并发问题
- key可以为null值吗?
- 一般用什么作为HashMap的key
- 当使用可变类作为HashMap的key有什么问题?
- 如果让你实现一个自定义的class作为HashMap的key该如何实现?
- HashMap和Hashtable的区别
- HashMap多线程操作导致的死循环问题
- ConcurrentHashMap和HashTable的区别
HashMap的实现原理
HashMap底层采用的是数组+链表实现的。一个Entry数组存储键值对,每一个键值对就是一个Entry实体。而Entry类实际是一个单向链表。在jdk1.8中规定了链表长度大于8的时候,链表转换为红黑树。
HashMap中数组和链表的作用
- 数组用于确定桶的位置,获得桶位置的计算方式是元素的key的hash值对数组长度取模。
- 链表解决hash冲突的问题。当hash值一样的时候就在数组上形成链表,而且才用的是头插法。什么是头插法?就是后进来的直接插入在头部。为什么呢?因为创始人觉得后进来的被访问的可能性更大。
hash冲突的解决方法
- 开放定址法
- 链地址法
- 再哈希法
- 公共溢出区法
可以用Linkedlist去替代数组吗?
可以的。但是在得到hash值的情况下,通过数组去确定桶的位置更快。
HashMap的扩容条件
如果bucket超过了load factor * current capacity,
就要resize
load fator = 0.75
,为了最大可能的避免哈希冲突。
current capacity
为当前数组大小
为什么扩容是2的次幂
- HashMap为了存取高效,要尽量较少碰撞,就是要尽量把数据分配均匀,每个链表长度大致相同,这个实现就在把数据存到哪个链表中的算法;这个算法实际就是取模
,hash%length
。
但是,这种运算不如位移运算快。
因此,源码中做了优化hash&(length-1)
。
也就是说hash%length==hash&(length-1)
所以,保证容积是2的n次方,是为了保证在做(length-1)
的时候,每一位都能&1
。
HashMap的put元素过程
- 对key的hashCode()经过扰动函数处理得到hash值,然后通过(n-1)&hash判断当前元素存储的位置。
- 如果没有碰撞,直接放入bucket里
- 如果碰撞了,就判断该元素与要存入的元素的hash值以及key是否相同。如果相同就更新value,保证key的唯一性。如果不同就通过拉链法解决冲突。以链表的形式存入bucket后。
- 如果链表中的元素达到了8个,就转为红黑色。
- 如果bucket满了就resize
HashMap的get元素过程
- 根据key的hashCode()进行hash运算,计算index
- 如果在bucket中第一个节点就命中了就返回;如果有冲突使用key.equal(k)去查找对应的Entry。
你知道哪些hash算法
- Hash函数是指把一个大范围映射到一个小范围