java8中的HashMap源码分析(四):总结

Java架构师交流群:793825326

java版本:jdk1.8

IDE:idea 18

上一篇(https://blog.csdn.net/dap769815768/article/details/96584394)我们分析了HashMap的get方法,下面我针对目前的源码分析对HashMap做下总结:

1.往链表里面追加数据是从尾部加入,即

p.next = newNode(hash, key, value, null);

红黑树亦是如此,因此红黑树操作仍然用到了next这个属性。

2.hashmap由于每次存入数据都要进行扩容和链表转红黑树检查,所以存数据的速度很慢,通过控制加载因子可以控制扩容的频率,那么一般场景下什么样的加载因子合适呢,参考如下:

a)加载因子越大,越会频繁地触发链表转红黑树操作,而扩容的操作次数则越小,空间利用率加大,这个时候hashmap更像是一个链表或者红黑树,涉及到的链表和红黑树查询变多,需要比较key是否相等的次数变多,查询速度降低,存储速度快。

b)加载因子越小,触发链表转红黑树的概率越低,扩容操作次数则越大,空间利用率降低,这个时候的hashmap更像是一个数组,涉及到的链表和红黑树查询较少,查询速度变快,存储速度慢。

c)官方的默认值是0.75,这个是空间利用率和效率的一个折衷,至于为何0.75是最佳值,暂时我还不清楚。

综上,加载因子所决定地是hashmap的数组特性多一些还是链表/红黑树的特性多一点,哪个特性多一点,那么它使用起来就越接近哪种数据类型。

对此,我做了一个测试,如下:

 HashMap<String,String> map=new HashMap(8);
        long start=System.currentTimeMillis();
        for (int i=0;i<10000000;i++)
        {
            map.put(Integer.toString(i),Integer.toString(i));
        }
        System.out.println(System.currentTimeMillis()-start);

由于运行环境的差异,同样的代码可能每次打印的时间都不太一样,当使用默认加载因子的时候,上面的代码普遍打印的时间是11000-12000之间,当加载因子设置为1时,普遍打印时间为9000-10000之间。但并不是值越大越快,当值继续增大后,触发红黑树操作的概率就加大了,红黑树操作比链表要耗时,因此存入的时间会变长。

总结hashmap的存数据操作耗时的环节如下:

a)扩容操作

b)链表转红黑树操作

c)红黑树操作

d)key值的比较,加载因子越小,hash碰撞的概率越小,比较的次数越少,越省时(对应查询速度越快),但触发扩容操作的概率越高

由于HashMap的查询时间很快,不管采用多大的加载因子,一般都是ms级一下,所以这里面不比较加载因子对于查询速度的影响了

所以理论上来讲,要想提高HashMap的数据存入速度,在提前知道大概数据有多少的情况下,可以考虑实例化的时候设置容量来减少resize的次数,提高速度。但这个不是绝对的,参考如下测试代码:

        HashMap<String,String> map=new HashMap();
        long start=System.currentTimeMillis();
        for (int i=0;i<20000000;i++)
        {
            map.put(Integer.toString(i),Integer.toString(i));
        }
        System.out.println("存入时间:"+(System.currentTimeMillis()-start));

这段代码的普遍耗时大概在24000-25000之间,如果设定初始容量为20000000,代码耗时反倒变长了,因为数组变大,导致数组的寻址开销变多。如果使用5000000,则普遍执行时间在22000左右,效率会提高一些。所以初始容量的选择,要以尽量减少扩容为原则,尤其是后期的扩容,当数据量很大的时候,每次扩容都将耗费大量的时间,但是容量太大,数组的寻址则会变得很耗时,所以初始容量的设定还要兼顾尽量减少寻址耗时。

另外这个还和你的数据有关,如果你的数据恰好规律一致,经过hash计算后,全部发生了碰撞,那么就会有大量的红黑树操作,这个时候的查询速度显然不会太快,录入速度也不会有明显的提升。因为要进行大量的key的比较。

因此总结下来,初始容量和加载因子配合使用,才能有效控制扩容次数,控制寻址时间,控制红黑树的操作频率等,而这些算是比较耗时的操作。

所以我的建议是,使用HashMap的时候,加载因子尽量使用默认的0.75,这大概是官方通过实验得出的一个比较理想的数据,初始容量的设置尽量接近实际数据。

3.链表转红黑树的触发条件是,链表的节点数大于等于8并且hash桶的大小大于等于最小树形化阈值64的时候,才进行红黑树转化,否则就进行扩容操作。所以理论上来讲,可能存在链表长度大于等于8 的情况,因为可能扩容后,原本的整条链还在同一索引位置上,并没有被拆成两条链。如果在等于8的情况下往该条链追加数据,由于尚未达到树形化条件,那么则继续扩容,但继续扩容仍然未能将原本的两条链拆开,这个时候链表长度便为9。(这里涉及到hash算法的问题,需要验证)

4.扩容的时候,可能存在某个数的节点个数很小的情况,则进行红黑树转链表操作,这个阈值是6。

5.put方法会返回旧值。

6.hashmap查询快,插入慢。适合的场景是大数据情况下的查询操作,而不是大数据情况下的插入操作。

7.get方法为何参数类型是object类型,而不是泛型E?这个问题暂时找不到答案,后面再补充。

Python网络爬虫与推荐算法新闻推荐平台:网络爬虫:通过Python实现新浪新闻的爬取,可爬取新闻页面上的标题、文本、图片、视频链接(保留排版) 推荐算法:权重衰减+标签推荐+区域推荐+热点推荐.zip项目工程资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松复刻,拿到资料包后可轻松复现出一样的项目,本人系统开发经验充足(全领域),有任何使用问题欢迎随时与我联系,我会及时为您解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(如有)等。答辩评审平均分达到96分,放心下载使用!可轻松复现,设计报告也可借鉴此项目,该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的。 【提供帮助】:有任何使用问题欢迎随时与我联系,我会及时解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 【项目价值】:可用在相关项目设计,皆可应用在项目、毕业设计、课程设计、期末/期/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 下载后请首先打开README文件(如有),项目工程可直接复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值