HashMap笔记 hashmap高级

HashMap底层原理

JDK1.7数组+链表
JDK1.8数组+链表+红黑树
首先HashMap底层是一个数组加链表的数据结构,数组里存放的是一个个Node节点,每个Node里面包含了key,value,还有一个next属性,这个next指向下一个Node,这样就完成了一个链表结构。HashMap在添加一个(key,value)时,首先会对key进行hash运算(能够让数据更均匀的分布到数组上),获取一个hash值,该值就和数组长度进行取余后获取此数据的下标索引,这时会判断数组在该下标下是否有node对象,如果有,会调用eques()方法比较两个key是否相同,如果相同,则做覆盖操作,如果不相同,则查看node节点中next是否为空,如果为空,就直接将存入的{key,value}封装到一个node节点中,然后把该对象赋值给next变量,如果next不为空,继续进行对比。在这个过程中,负载因子0.75会和数组的默认长度16的乘积进行判断是否扩容,如果超过12就会扩容,就会左移一位(扩大一倍),
在jdk1.8之后HashMap做了相关优化,它定义了两个变量,一个是树阈值,一个是树还值,分别为8和6,当链表长度大于等于8时,会将链表结构转换成一个平衡的二叉树结构,也就是红黑树结构,那如果做删除操作时,当树节点数小于等于6时,则还原为链表结构,这样,提高了检索速度。
选择8因为符合泊松分布,超过8的时候,概率已经非常小了,所以我们选择8这个数字。

HashMap线程安全吗?

HashMap线程不安全,在jdk1.7的时候,采取的是头插法,在多线程进行put的时候,如果索引值相同,链表的元素会倒置,顺序就乱了,就可能发生环行链和数据丢失的问题,导致cpu利用率100%的问题。在JDK1.8中,HashMap进行了优化,把头插法改成了尾插法,所以就不会出现环形列表的情况,但是在多线程的情况下经行put,如果hash值一样,,该位置数据为null,当A线程进入后还没有进行数据插入时被挂起,而线程B正常执行,B数据正常插入了,然后线程A获取cpu时间片,此时线程A不会在进行hash判断了,线程A会把线程B插入的数据给覆盖,导致数据发生覆盖,发生线程不安全。
会报java.util.ConcurrentModIficationException并发修改异常:一个线程正在写,另一个线程过来抢
解决办法:
第一种:使用HashTable
HashTable是线程安全的,它实现代价太大,它是给整个hash表加了一把大锁,多线程访问的时候,只要有一个线程访问,其他线程只能阻塞
第二种:工具类
Map<String,String> hashMap=Collections.synchronizedMap(new HashMap<>()) 和Hashtable一样,实现上在操作HashMap时自动添加了synchronized来实现线程同步,都对整个map进行同步,在性能以及安全性方面不如ConcurrentHashMap。
第三种:使用ConcurrentHashMap
它避免了全局加锁,改成了分段锁(synchronized)
第四种:使用写实复制:CopyOnWrite(争对list和set,hashMap没有)
网容器中添加数据时,不直接添加,先把元素复制出来放到一个新的容器中,在新的容器中添加数据,添加之后,在将原来容器中的引用指向新的容器,不需要加锁实现了并发读,实现了读写分离,但它只能保证数据的最终一致性,不能保证实时一致性

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值