【集合类】HashMap几个常见问题

hashMap的底层存储结构是?

在1.7是由数组+链表组成,1.8是数组+链表+红黑数

为什么要进行树化?

链表取元素要从头遍历到对应的节点,时间复杂度是O(n),而红黑树基于二叉树时间复杂度是O(logN),从而提高搜索效率。但是红黑树节点的大小约是普通节点的两倍,只有包含足够节点时进行转化才比较合适,这是出于空间与时间平衡的考虑

为什么树化阈值是8?

如果hashCode分布离散足够好的话,一般很少会进行树化,在理想情况下,链表长度符合泊松分布,各长度的命中概率递减,而8是的概率就已经足够小了

为什么退化为链表的阈值是6?

因为如果阈值是7,那么删除一个节点就要退化,增加一个节点就要树化,频繁的转变降低性能,所以才不设置太临界

为什么hashMap的扩容要是二倍扩容?

因为路由算法是 (length-1) & hash(key)

扩容必须二倍是为了维持容量始终为2的幂次方,这样在进行hash散列的时候,因为length - 1的后几位都是1,在与hash做&运算时得到的结果可能为1也可能为0,增加了散列的不确定性,减少hash碰撞

HashMap为什么线程不安全?

1.7的线程不安全是在扩容方法可能产生死循环,1.8的线程不安全是在put方法,当两个元素hash碰撞时可能导致数据被覆盖并且在扩容之前的判断++size > threshold并不是一个原子操作

jdk1.7的HashMap为什么会导致死循环?

在并发环境下,1.7的扩容方法是采用头插法将元素迁移到新数组中,由于头插法会导致链表翻转,从而照成两个节点的next相互指向导致死循环

hashcode是唯一的吗?插入元素的时候怎么比较的?

不是唯一的,当桶中不存在元素时直接放入,如果存在则比较桶内元素的Key是否与当前节点相同,相同则直接更新,不同则判断是否是红黑树节点,如果是进行红黑树节点的处理,如果不是则代表是链表节点,从头节点开始比较,如果存在Key相同的则更新,如果直到尾结点还没匹配,则将当前节点作为尾结点插入

HashMap跟HashTable,ConcurrentHashMap有什么区别?

HashMap线程不安全,允许null键null值

HashTable线程安全,不允许null键null值,所有方法都是用synchronized修饰

ConcurrentHashMap线程安全,不允许null键null值,1.7采用分段锁机制,而jdk1.8采用的是CAS+synchronized

为什么CHM不允许空键呢?

这是因为在map的get方法返回null时存在二义性,并不能确定是没有该key还是存在该key但是值为null,这时就需要containsKey()来判断,但是在多线程情况下,该判断是不安全的,可能在第一个线程进行判断的时候第二个线程就put(key, null)了,而在非并发安全的map中则不需要考虑这点。

至于value为什么不能为空,没找到合理的设计依据,可能只是设计者的个人偏好?

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值