HashMap底层相关内容

HashMap的底层结构:
1.7之前 数组加链表,当两个值进行插入的时候 采用头插法进行插入,可能会造成死循环
1.8之后 数组加链表/红黑树,当两个值进行插入的时候,采用尾插法进行插入,不会造成死循环

HashMap底层put源码:

  • 1.7之前 会先进行判断当前插入的值的key是否为null,如果是null
    检查数组为0的索引下标位置是否为空,如果不是空,就会把原来的值取出来放入oldValue返回回去,并采用头插法将新put的值放到原来的位置上,旧的值放到新值下面。
    如果是空,就会直接把put的值放入索引下标的位置上。
    如果key不为null会根据key调用方法indexFor计算出当前key所要存储的数组的索引下标的值,在判断当前下标下是否有元素,重复上面的操作,
    如果有的话还要进行判断当前的key是否存在,如果存在就直接覆盖并返回之前的值。
    之后将modCount加一,为了判断集合在迭代的时候,值是否发生了修改
    之后就会添加一个新的链表节点,添加之前先判断当前的数组用了多少,
    如果当前已用的数组位置达到了数组长度的0.75并且即将添加位置不为空就会发生扩容机制,没有达到会头插法添加元素,size++
  • 1.8之后 会先进行hash运算取出hash值,再判断当前hashMap的数组是否为空,如果为空初始化数组
    判断当前数组索引位置的值是否为空,如果为空,新建加入一个节点
    如果不为空,判断当前索引下标位置的元素是否与新put的值的key相同,如果相同,将值取出来放到e中
    如果不相同,判断当前索引下标位置的节点是否是树节点,如果是,去调用树方法去判断 加入节点等
    如果不是树节点,表示是链表节点,进行循环遍历,将p.next给e,如果e是空的话,直接将put进来的值插入到p.next位置,并判断是否已经达到了树化阈值
    达到了树化阈值了判断数组长度是否达到了64,数组长度也达到了进行树化,跳出循环
    如果e不为空,判断e的hash值是否和put进来的值的key的hash一样,如果相同,直接跳出循环
    将e的值放到一个oldValue中进行返回
    修改modCount的值进行++,size进行++,判断size的值是否达到了扩容情况,进行扩容。

HashMap在1.8的时候为什么要转换为红黑树?
因为在1.7的时候如果出现hash冲突就会将冲突的数据加入到链表中,如果冲突的数据多了,链表的数据就会太长,这样的话就会造成查询效率下降
红黑树的查询效率大于链表。红黑树的加入是为了解决hash冲突,提高查询效率。

树化阈值为什么是8?
这涉及到一个泊松分布,有人调查过,如果阈值达到8的时候,造成hash冲突的概率是千万分之一级别,冲突的概率就已经很小了

反树化阈值为什么是6?
因为如果反树化阈值是8,这样不太稳定,如果数组的同一节点在加上一个数就会导致进行树化,而如果减少一个数就会进行链表化,这样来回树化和反树化,会导致性能降低
而如果反树化阈值太小,很少的数就会导致转换成红黑树,完全没必要

加载因子为什么是0.75?
这是对时间和空间之间取得一个折中值,如果太大了是1的话,会导致hash冲突增加,降低性能,浪费时间
如果太小,是0.5的话,会导致还有一半的空间没有用就进行扩容,浪费空间

hashmap默认初始化大小是多少?为什么按照2的幂次方进行扩容?
初始化大小是16,因为2的幂次方-1是多个1的组合,就像1111,当获取添加元素到数组的哪一个索引下标的时候用hash码的值和2的幂次方-1进行取余运算,因为是取余运算
在相同的位置上,hash码值是什么,结果就是什么,降低了得到同一索引下标的可能性,也就降低了hash冲突。

hashmap扩容的条件?
当数组中插入元素的长度达到数组的长度*加载因子的时候会进行扩容
当同一个索引下标位置链表的长度达到8并且数组的长度还没有达到64的时候

hashmap是怎么进行扩容的?1.7
hashMap的初始化容量如果不是2的幂次方,实际初始化容量会是接近自定义初始化容量的最小的2的幂次方
按照原数组长度的2倍进行扩容,会将原来的数组的索引下标位置的部分元素转移的原数组索引下标位置加上原数组的长度的位置上
先进行循环遍历要进行转移的数组的索引下标,里面在进行循环遍历要转移的数据
将e指向要进行转移的元素,将next指向e的下一个元素,在将e.next指向要转移的新数组的对应下标位置,再将e赋值给新数组的对应索引下标,相当于是往下移了一位,
再将next的值赋给e,再次执行同样的操作。

为什么hashMap在1.7的时候采用头插法,在1.8变成了尾插法?
因为在高并发情况下,在扩容进行数据转移的时候,使用头插法可能导致next指向已经转移到新数组的数据,再次使用e.next指向新数组索引下标的时候,
的是链表上面的元素,导致成为一个环,造成了死循环。
而使用尾插法不会导致这样的情况。

hashMap里面的key和value可以是空值吗?
key和value都可以是空值,但是key只能有一个是空值

HashMap是线程安全的吗?
不是线程安全的,但是可以通过使用Collections的synchronizedMap()变得线程安全

HashMap和HashTable的区别是什么?
HashMap是线程不安全的,HashTable是线程安全的
HashMap允许Key和Value为空,HashTable不允许键或值为空,如果键为空会报空指针异常
HashMap遍历的顺序不确定,HashTable的遍历顺序是按照插入的顺序进行遍历的
HashMap的性能相对较高一点,HashTable的性能相对较低

HashMap和LinkedHashMap的区别?
HashMap是无序的,获取的顺序和插入顺序不一致,LinkedHashMap是有序地,获取元素的顺序和插入顺序是一致的
HashMap底层就是数组加链表/红黑树,LinkedHashMap的底层是在HashMap的基础上维护了一个双向链表

HashMap和ConcurrentHashMap的区别是什么?
HashMap是线程不安全的,ConcurrentHashMap是线程安全的
HashMap由于线程不安全,所以并发情况下可能导致数据不一致或抛出异常,ConcurretHashMap使用了分段锁,所以不同的段可以被不同的线程访问,提高了并发性
HashMap没有锁,要保证线程安全需要再外部加上同步机制,ConcurrentHashMap使用了分段式锁, 锁得到粒度小
HashMap:在扩容时需要重新计算哈希值,重新分配元素位置,可能会导致链表长度过长,影响性能。ConcurrentHashMap:在扩容时只需要对某个段进行扩容,不会影响其他段,减少了扩容带来的性能损耗。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

黑妖问路

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值