java之hashmap

最近看了一些HashMap相关的教学视频,生怕以后忘了,所以赶紧把自己理解到的整理了一下,比较通俗易懂,可能列的还是比较乱,后续会慢慢优化完善,

特点:

  • 无序、键值都可为空、键唯一、线程不安全

数据结构:

  • jdk1.8之前:数组+链表
  • jdk1.8之后:数组+链表+红黑树
  • 为什么引入红黑树(变色、左旋、右旋):将查找的时间复杂度从o(n)提升到o(logn)
    HashMap

存储方式:

  1. 根据键的hashCode、数组长度length结合某种算法计算出该数据在数组中存放的位置即索引;(某种算法:hashCode & (length - 1) = hash % length,前者二进制数位移效率较高,后者取余效率较低。)
  2. 如果该位置没有数据则直接插入,如果有数据再比较两个数据键的hashCode;
  3. 如果两个数据键的hashCode相同,此时发生哈希冲突(碰撞);
  4. 再比较键的内容,如果内容也相同则直接替换值,否则以链表方式存储;
  5. 当链表长度大于8并且数组长度小于64,数组扩容(之前的2倍);
  6. 当链表长度大于8并且数组长度大于64,此时链表才转为红黑树;
  7. 当链表长度大于8转成红黑树,小于6再转回链表。
  • 由于树节点(红黑树,TreeNodes)的占用空间是普通节点(链表,Nodes)的2倍,所以上述选择6、8作为阈值是因为符合泊松分布(统计与概率学),说白了就是时间和空间的权衡。

链表节点插入:

  • 1.7:头插法
  • 1.8:尾插法

扩容(x2):

  • 1.7:存放数据大于临界值(负载因子*容量)时发生扩容,indexfor()重新计算下标;
  • 1.8:存放数据大于临界值(负载因子*容量)时、容量小于64且链表长度大于8时发生扩容,根据规律(扩容之后的容量二进制位把1往前移了一位,所以根据计算规则hash&(n-1),减1高位为0后面全是1,以减1之后的高位为参考点与hash值对比)当hash当前参考位为1则索引为原索引+原容量,为0则索引为原索引。
  • 0.75:默认负载因子,16:默认容量(最大2的30次幂)
  • 为什么容量必须为2的n次幂:尽量让数据能在数组中均匀分布,尽量避免哈希碰撞。而且就算不是2的n次幂,内部方法也会重新赋值为大于给的最小2的n次幂。

线程不安全:

  • 1.7:并发执行扩容操作时会发生环形链表和数据丢失的情况
  • 1.8:并发执行put操作时会发生数据覆盖的情况
  • hashmap:线程不安全
  • hashtable:线程安全,全局只有一把锁(synchronize),线程竞争激烈时效率低
  • concurrenthashmap:线程安全,分段锁技术,并发度较高
  • 所以如果考虑线程安全使用concurrenthashmap,否则使用hashmap足够了,不推荐使用hashtable

算法:

  • 无符号右移(>>>):二进制数往右移动多少位,前面补0
  • 按位与(&):相同的二进制数位上,都是1的时候,结果为1,否则为0
  • 按位异或(^):相同的二进制数位上,数字相同,结果为0,否则为1
  • 上面的算法都是些位运算,运算效率比较高;还有平方取中法、取余法、伪随机数法等运算效率比较低。
  • 其实像扩容、哈希碰撞(链表、红黑树)这些都是比较耗费性能(时间和空间)的,所以官方才会采用一些算法、初始化阈值,临界值等去尽量避免这些。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值