今天一起说说并发容器类,实际上还是JDK代码里面的东西,其实不管是Map或者ConcurrentMap,网上太多的资料了,其实有些资料也是从网上找的,但是加入了自己的理解,更易懂的方式展示给的大家,技术点老铁们都是可以看懂的,但是里面的内部逻辑。
(一)JDK源码学习方法
- ① 介绍
逻辑思维能力是梳理学习方法的基础,养成先行思维,两个或者多个概念,像一条线穿起来。
- ② 推导法
1.因果推理
针对JDK写出来的代码,进行跟踪梳理。如果JAVA中网络编程只提供了BIO和NIO两种方式,所以一切框架中,涉及到网络处理的,都可以用这两个知识点去探究原理。万丈高楼平地起,盖房子都是从下往上的,学习知识也是一样。
2.归纳总结
根据经验产生可能正确的猜想,线上10台服务器,有5台总是每天会自动重启,收集相关信息后,发现是运维在修改监控系统配置的时候,漏掉了提高三台机器的重启阈值。
3.类比法
集群概念就好像马拉车,一匹马拉不动的时候,就使用多匹马去拉。分布式概念,就像是理头发的过程,洗头发和剪头发是不同的人复杂。
(二)推理HashMap的实现
- ① 介绍
HashMap就是往里面存东西,存放的方式就是键值对的概念。key用来查找。
*② 考虑存储的介质
数据存储分为:链表,树,队列,图。。。具体HashMap呢,看看源码
HashMap内存用于存储的数据结构是:数组
- ③ 如何存储的,往数组里面存数据有什么办法
key和value如何存放的,放数组里面放入数据有什么办法,其实就是数组的插入和查找,其实设计到一些简单的基础的算法。
方法(一)
HashMap里面,记录当前位置,key和value插入到数组的第0个,第1个,第2个等位置。按照顺序一个一个比较,当key想通的时候,需要直接覆盖掉。查询的时候也需要一个一个取遍历,这样的一个做法,顺序查找,顺序插入。
方法(二)
一个一个顺序查找太慢了,可以进行二分查找,从中间开始查,分成2份或者分成N份,运气好一下就查找了,但是运气不好呢,查到最后一个才查找出来吗?减少复杂度
方法(三)
分块查找:二分查找和顺序查找的一种改进。
方法(四)
哈希表:对元素的关键信息进行hash计算,求出下标后直接插入或者查找。常见的实现就是除留余数法。
如果你的技术点达不到上边想到的几种存储数据的方法选择,也有个技巧,只能跟进下代码,分析看看内部是什么流程,直接看官方的API注释,熟悉代码的蛛丝马迹。
④ 如何put到hashMap中,JDK1.8的方式
1、对Key求Hash值,然后再计算下标。
2、如果没有碰撞(存在,链地址法),直接放入桶中(碰撞的意思是计算得到的Hash值相同,需要放到同一个bucket中)、
3、如果碰撞了,以链表的方式链接到后面。
4、如果链表长度超过阀值( TREEIFY THRESHOLD==8),就把链表转成红黑树,链表长度低于6,就把红黑树转回链表。
5、如果节点已经存在就替换旧值。
6、如果桶满了(容量16*加载因子0.75),就需要 resize(扩容2倍后重排)。
链地址法
将全部具有同样哈希地址的而不同keyword的数据元素连接到同一个单链表中。假设选定的哈希表长度为m,则可将哈希表定义为一个有m个头指针组成的指针数组T[0..m-1]。凡是哈希地址为i的数据元素,均以节点的形式插入到T[i]为头指针的单链表中。而且新的元素插入到链表的前端,这不仅由于方便。还由于常常发生这种事实:新近插入的元素最优可能不久又被访问。
- ⑤ HashMap的总结
- 数组的大小一定是2的N次方。
- 为什么HashMap是线程非安全的,其实就一点,扩容的时候链表进行i++,i++这个操作是非原子性的,必然不是线程安全的,说这一点就够了。
- 合理控制数组和链表的长度,动态扩容resize()。
PS:JDK现在都出到13了,如果你不掌握太多的方法,单独的会用,不了解底层是不行的。别人能很快掌握,还是内功修炼的好数据结构与算法学的好,目前出了个专题【数据结构与算法】需要的可以点击头像进入专题。