Java HashMap 简介

HashMap 简介

  • HashMap 主要用来存放键值对,它基于哈希表的 Map 接口实现,是常用的 Java 集合之一,是线程不安全的。 HashMap;
  • 可以存储 null 的 key 和 value ,但 null 作为 key 只能有一个,null 作为值可以有多个;
  • JDk 1.8 之前 HashMap 底层是由数组+链表实现的,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的(拉链法解决冲突)。JDK 1.8 及之后的 HashMap 在解决哈希冲突时使用了红黑树,当链表长度大于等于阈值(默认为8)并且数组长度超过64时(未超过64会先将数组扩容),将链表转化为红黑树,以减少所搜时间;
  • HashMap 默认的初始大小为16,负载因子为0.75,数组使用超过 16 * 0.75 =12 后就将进行扩容,每次扩充容量变为原来的2倍。并且,HashMap 总是使用2的幂作为哈希表的大小;
    在这里插入图片描述

为什么使用红黑树

  • 在使用红黑树之前,即使哈希函数取值再好,也很难达到元素完全均匀分布,当 HashMap 中有大量的元素都存放在一个数组节点时,这个节点下就有一条长链表,这时候 HashMap 就相当于一个单链表,查找的时间复杂度为O(n),失去了其优势;
  • 针对这种情况,JDK 1.8 中引入了红黑树(红黑树的查找时间复杂度为:O(logn) )来优化这个问题,当链表长度很小的时候,即使遍历,速度也很快;

为什么单链表长度超过8时才转为红黑树结构

  • 因为树节点所占空间是普通节点的两倍,所以只有当节点足够多的时候,才会使用树节点。
    在这里插入图片描述
  • 源码上说,为了配合使用分布良好的 hashCode ,树节点很少使用;
  • 在理想情况下,受随机分布的 hashCode 影响,链表中的节点遵守泊松分布,而且根据统计,链表节点数是8的概率已经非常小了,只有在这种比较罕见和极端的情况下,才会把链表转变为红黑树;
  • 链表转化为红黑树也是需要消耗性能的;

为什么 HashMap 线程不安全

  • 非同步操作:HashMap 的实现不是线程安全的,它没有内部机制来处理多个线程同时访问或修改 HashMap 的情况。如果多个线程对 HashMap 进行插入、删除或更新操作,可能会导致数据的不一致或损坏;
  • 容量扩容:HashMap 在扩容时,需要重新计算元素的哈希值并重新分配存储位置,这个过程涉及到对原数组进行复制和重新插入元素的操作。如果在扩容期间有其他线程对 HashMap 进行并发修改,就可能导致数据丢失或出现异常;
  • 非原子操作:HashMap 的操作不是原子性的,例如 put() 方法涉及到了多个步骤,包括计算哈希值、查找和插入元素等。如果多个线程同时执行这些操作,就有可能导致数据不一致的情况

解决方法:

  • 使用同步机制:可以使用线程安全的 Map 实现,如 ConcurrentHashMap ,或者通过在访问 HashMap 时使用 synchronized 或其他锁机制来确保同一时间只有一个线程能够修改 HashMap;
  • 使用线程封闭:可以将 HashMap 封闭在单个线程中,通过使用 ThreadLocal 或将 HashMap 作为局部变量在每个线程中进行操作,从而避免多线程访问导致线程安全问题;
  • 7
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值