HashMap1.7和1.8的put()自总结以及线程安全的ConcurrentHashMap

HashMap底层基于hashing原理,key和value都可以是null,通过put()和get()储存获取对象,HashMap的优势就是crud的时间复杂度都是 O1)所以速度快
get:调用getNode(hash(key),key)获取位置判断数组非空数组大于0,头节点是否为空。头结点满足返回条件?不满足则判断桶中不止一个节点,next否红黑树,不是就遍历寻找节点equals
HashMap.put():
根据key通过哈希算法与运算得出数组下标,判断map为空则初始化↓
1.7(数组链表时间复杂度O(n)如果数组下标位置元素不为空,则会先判断当前插入元素hash值以及两者key是否一致返回原值value。else:存数据前判断扩容:存放新值时map元素的个数>=阈值(默认16负载因子0.75阈值12且存放entry的时候数据发生hash碰撞(当前key计算的hash值换算出来数组下标位置已经存在值就调用resize()扩容为两倍:transfer()把原来数组元素放入新数组,后生成Entry对象头插法添加到当前位置的链表中
1.8(数组链表红黑树(二分法搜索时间复杂度O(log n)如果数据下标位置元素为空,则将key和value封装为Node对象(hash key value next)放入该位置。不为空判断当前Node的类型是红黑树Node(TreeNodes)还是链表Node
红黑树Node将key和value封装为一个红黑树节点并添加到红黑树中去 过程中判断红黑树中存在当前key则更新value
Node对象是链表节点将key和value封装为一个链表Node并通过尾插法插入到链表的最后位置去,尾插法需要遍历链表中判断是否存在当前key存在则更新value,当遍历完链表后将新链表Node插入到链表尾存数据后判断扩容treeifyBin():如果链表长>=8后再判断数组长度未超过64就 扩容为两倍否则:会将该链表转为红黑树,当红黑树节点小于或等于6以后会恢复成链表形态(中间隔个7防止两者频繁转换
使用头插法的原因:新加入的键值对被查找访问的概率更高
使用尾插法的原因:因为尾插法可以统计链表长度是否大于8转化为红黑树
使用红黑树的原因:相比于平衡二插排序树,红黑树只有在最长路径>最短路径*2时才触发旋转操作。时间复杂度O(log n)优于O(n)

Hashmap1.7扩容前也有可能存储更多值(超多16个值,最多可以存26个值)都还没有扩容。原理:前11个值全部hash碰撞,存到数组的同一个位置(虽然hash冲突,但是这时元素个数小于阈值12,并没有同时满足扩容的两个条件。所以不会扩容),后面所有存入的15个值全部分散到数组剩下的15个位置(这时元素个数大于等于阈值,但是每次存入的元素并没有发生hash碰撞,也没有同时满足扩容的两个条件,所以叶不会扩容),前面11+15=26,所以在存入第27个值的时候才同时满足上面两个条件,这时候才会发生扩容现象


关于为什么HashMap的长度包括扩容后的长度都是2的次方这个问题
这个indexFor方法逻辑给出了答案。因为2的次方减一即(length - 1)得到的数其二进制样式差不多,前面有多少个0不一定,但后面一定是连续的1。比如(2-1)的二进制是000001,(2的三次方-1)的二进制是000111。而&(与运算)原则是有0就是0,都是1才是1。想象一下,比如000111这种二进制参与与运算,前三位即0的位置不管另一个二进制上对应位是什么都是0,即一种情况,而后三位即1的那位得看另一个二进制对应位置上是什么,即两种情况。很好理解,1越多,情况越多,即求出来的数组下标可能性越多,即散列越均匀,形成的链表越少,查询效率越高。理解这个小知识点很重要。

ConcurrentHashMap
与HashTable最大的区别就是ConcurrentHashMap对大table中每个位置加了锁 键值都不允许为null table数组为2的幂次 key均匀分布,减少hash冲突
对于SynchronizedMap封装HashMap的put方法加上互斥锁保证安全性,CHM在1.8中取消了segments字段,采用了CAS+volatile
1.7版本中cmap的put先判断是否需要扩容后根据key找到对应的Segmentm,再往Segment中加锁的put键值对,利用自旋锁去尝试获取锁,利用再入锁的方式锁住Segment,保证只有一个线程操作Segment,不会形成环形链表
1.8取消了Segment,第一层是HashEntry。当链表达到一定长度会以红黑树代替和HashMap一样.put的时候采用了CAS+synchronized保证线程安全
putVal3参冲突是否替换,KV非空判断后空数组首次initTable懒汉式扩容,非初始化扩容时发现正在扩容,且当前元素所在的桶元素已经迁移完成了就协助扩容然后数据迁移,最后加S锁put值跟hmap一样
初始化扩容用CAS将SIZECTL值修改为-1表示正在扩容后double check数组是否初始化后初始容量16

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值