HashMap

原理

存储key-value对,加快查找速度,是非线程安全的哈希散列,其底层原理是数组+链表+红黑树。

红黑树是JDK1.8新加进来的,为了解决哈希冲突太严重导致查找时间复杂度变为O(n)的问题,当链表长度大于8时,链表会自动转换为红黑树,当链表长度小于8时,红黑树会自动退化为链表。注:红黑树是一棵高度平衡的二叉搜索树,时刻维持树高为logn,可以保证查找性能为O(logn),在插入或删除操作时通过“旋转”维持树高。

hash冲突的解决方案

开放地址法

  • 线性探测:从冲突位置依次向后寻找空闲位置,会有聚集的问题,聚集是指table比较满的时候,需要频繁探测插入位置
  • 二次探测:每次探测的步长是探测次数的平方,线性探测每次探测的步长是1,可以探测比较远的单元,解决聚集的问题,但是会发生二次聚集,二次聚集不严重,但二次探测不经常使用
  • 再哈希法:再使用一个哈希函数计算探测步长,不同的关键字对应不同的步长,避免聚集

链地址法

每个table元素都是一个链表,发生冲突新增链表节点即可

公共溢出区

HashMap的put操作

(建议结合源码来看,思路会更清晰,参考https://blog.csdn.net/woshimaxiao1/article/details/83661464

  1. 若table的size为0,先初始化table,默认初始化大小为16;
  2. 如果key为null,则直接保存到table[0]的位置;
  3. 否则,计算哈希值,在哈希值的计算过程中加入了位运算的操作,尽量让更多位参与到运算过程当中,得到更加均匀的散列结果;
  4. 根据哈希值计算索引i,计算方式为h&(length-1),HashMap中table的长度必须为2的n次幂,其中一个原因是h&(length-1)==h%length,位运算可以大大提高计算速度;
  5. 判断table[i]是否为null,如果为空,直接插入即可;
  6. 如果table[i]不为空,判断key是否存在,如果存在直接替换value的值(先判断当前链表长度是不是超过8了,超过要执行树的查询,否则执行链表的查询);
  7. 如果key不存在,则遍历红黑树或链表并插入;
  8. 插入完成后如果++size>threshold,则执行扩容操作,其中threshold==LoadFactory*capacity。

HashMap扩容

HashMap扩容时要将容量扩充为当前容量的2倍,扩容后每个元素对应的索引会发生变化,因此需要重新计算索引,移动元素到相应位置。

JDK1.7的底层数据结构是数组+链表,在扩容时采用头插法移动元素,会导致元素顺序发生改变,多线程同时扩容时可能会死循环,因此是线程不安全的。JDK1.8改成了尾插法扩容,解决了死循环的问题(但是我看其它的文章指出,尾插法并不能完全解决线程安全问题,这一点我也不太确定)

HashMap长度为2的次幂的原因

  1. 索引的计算方式为h&(length-1),h&(length-1)==h%length,位运算比取余计算高效很多。
  2. 扩容时将旧的table移动到新的table时更方便计算索引,索引的计算方式为h&(length-1),扩容后length-1最后一位0会变为1,计算新的索引时,索引要么不变(与length-1最后一位1对应的那1位为0),要么变为原位置移动2次幂后的位置(与length-1最后一位1对应的那1位为0)。
  3. length-1低位全为1,因此获得的数据索引会更加均匀,碰撞的概率变小,查询速度会更快。

HashMap&HashTable&ConcurrentHashMap

HashMap:key和value可以为空;继承自AbstractMap类,实现了Map接口;非线程安全

HashTable:key和value不能为空;继承自Dictionary类,实现了Map接口;阻塞的,通过锁住所有数据实现线程安全;总是能拿到更新后的数据,效率比较低,不建议使用。

ConcurrentHashMap:实现了Map接口;非阻塞的,更新的时候只锁住部分表,读取的时候完全并发,线程安全;合理调度的情况下效率比较高,但是读取操作不能保证反应最近的更新。

  • JDK1.7通过分段锁的方式实现,默认分段数量为16,可以达到比较好的并发性能
  • JDK1.8之后通过CAS+Synchronized+ReenTrantLock实现
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值