目录
1、jdk1.8之前HashMap的底层结构和存在的问题
2、jdk1.8对HashMap做了哪些优化
3. HashMap的扩容机制
4. HashMap的hash算法、寻址算法优化问题
5. HashMap如何解决hash碰撞?
6. 工作中你怎么使用HashMap
大家好,我是四九城最豪横的小耳朵。
今天咱们来用大白话聊聊HashMap面试相关的知识点。
1、jdk1.8之前HashMap的底层结构和存在的问题
底层结构:jdk1.8之前HashMap的底层结构使用的就是数组加链表,它数据节点是一个Entry节点,Entry就是hashmap的一个内部类;
存在的问题:jdk1.8之前HashMap的数据插入的过程是头插法。它使用头插法会造成什么问题呢?它的resize方法里面调用了一个transfer方法,对Entry进行了一个rehash,在这个过程中,可能会造成链表的一个循环,就可能在下一次get的时候出现一个死循环的情况;也有可能就是因为它没有加锁,所以他也有可能在多个线程并发的情况,不能保证数据安全。
2、jdk1.8对HashMap做了哪些优化
jdk1.8之后HashMap的结构使用的就是数组加链表加红黑树,它数据节点是一个Node节点。
数据插入过程是尾插法,不再是头插法,避免了链表死循环问题。
3. HashMap的扩容机制
初始化HashMap时,需要设置他的capacity,它的默认初始化容量是16,然后负载因子是0.75。 HashMap会计算出来一个扩容阈值threshold,如果当我put的时候我会先判断,我当前的size是不是大于这个阈值,如果大于的时候,它会扩容成原来的两倍,将原来的Entry节点进行rehash;
4. HashMap的hash算法、寻址优化问题
hash算法优化:h = key.hashCode() ^ (h >>> 16)(hash值异或hash值右移16位)。对每个hash值,在他的低16位中,让高低16位进行了异或,让它的低16位同时保持了高低16位的特征,尽量避免一些hash值后续出现冲突,大家可能会进入数组的同一个位置。即减少hash碰撞。 寻址算法的优化:(n - 1) & hash 。 数组的长度是2的n次幂,那么长度-1与hash的与运算和hash % 长度的结果是一样的,即 a = (n - 1) & hash ; b = hash % size; a == b; 但是与运算的性能比取模的性能好,目的是提高寻址的性能。即用与运算替代取模,提升性能。
5. HashMap如何解决hash碰撞? hash算法让高低16位进行了异或,可以减少hash碰撞,但是无法完全避免这种情况,如果真的发生hash碰撞,那怎么解决的呢? jdk1.7之前使用链表解决hash碰撞。如果对2个元素进行hash取模,位置都取在了同一个Entry节点,此时就会在这个Entry节点挂一个链表,链表里面放这2个元素。取其中一个元素的时候就去找到这个Entry节点,然后再遍历链表,拿到元素。
jdk1.8使用链表+红黑树解决hash碰撞。
为啥要使用红黑树这种结构呢?
因为如果链表长度很长,遍历起链表来性能会很差。所以在链表长度达到了一定长度之后,会将链表转换为红黑树,通过遍历红黑树去查找元素性能要更高一些。
6. 工作中你怎么使用HashMap
在业务场景中使用HashMap在内存中存储一些数据。比如导入excel的时候,先通过poi把每一行的数据先放到一个HashMap中,然后在遍历HashMap进行业务处理。
问:你刚刚说HashMap是线程不安全的,那你工作中怎么去优化呢?
我:可以get或put元素的时候加个synchronize关键字。
问: 你可以走了。
我:。。。。。。
重来!
问:你刚刚说HashMap是线程不安全的,那你工作中怎么去优化呢?
我(心理活动:面试官真鸡贼啊,给我下套):我工作中遇到并发场景,一般使用ConcurrentHashMap。
问: 为啥一定要用ConcurrentHashMap,你用hashmap,可以get或put元素的时候加个synchronize关键字啊
我:因为ConcurrentHashMap内部使用的是分段锁,只对同一个Entry节点进行加锁,它支持的并发度更高。
End
作者简介:豪横的小耳朵,一个豪横的程序员。想和大家一起在技术的世界里豪横,用技术的眼光去看待世界。欢迎扫描下方二维码,持续关注,一大波原创系列文章正在路上