如何理解集合接口实现类的扩容机制?

前言:在开发中常常用到各种集合类容器来存储数据,其有着各自的使用场景与不同的数据结构和实现方式,很多时候都需要了解其源码实现原理,包括数据结构,扩容方式等,感觉许多地方都在纯粹的分析源码,使人容易不易理解或忘记。所以,今天就从数据结构和设计思想上,来想一想几大集合实现类的扩容方式为何是这样的。

目录

java集合有哪些实现类?

它们是如何扩容的?

为什么选择这样的扩容方式?


java集合有哪些实现类?

先回忆下,主要分为四类:

List 列表,有序,可重复Queue 队列,有序,可重复Set 集合,不可重复Map 映射,无序,键唯一,值不唯一。参考搬运的下图:

集合的知识,可在参考链接中继续学习,我们就以最常用的ArrayList,LinkList,HashMap作为分析对象,来对比探讨下它们的扩容方式与设计思想。

它们是如何扩容的?

简单纯粹的看:

LinkList:1个一个的加;

ArrayList:1.5倍扩容;

HashMap:2倍扩容,负载因子0.75;

为什么选择这样的扩容方式?

先说为什么LinkList是扩容是1(权且当它是扩容),因为它是链表,对头。

那为什么ArrayList扩容是1.5倍,我扩容也是1行不行(当然行,就按需要的数量加),可是它是数组,频繁扩容会频繁申请内存空间 + 数组频繁复制,会导致效率很低。那为什么是1.5倍,而不是固定值10,20等,或者其他倍数2倍,3倍呢。主要是考虑到经验,以及节约内存空间。

说明:随着数组越来越大,那么它极有可能会有相同的量级更多的数据进来,固定值10或者100,则分别会在数据量很大和很小时造成频繁扩容或空间浪费。(类似Synchronized的偏向锁,操作系统读取数据时会预读取其附近的一些数据到缓冲区,经验概率)。那如果是2倍,3倍呢?太大的倍数当然更容易造成空间浪费,而1.5倍时,就能充分利用前面已经释放的空间(这里的确想的很周到,详解参考链接)。如果k >= 2,新容量刚刚好永远大于过去所有废弃的数组容量。且比起1.6,1.7运算起来更方便(扩容时,容量用移位操作计算),故选择1.5倍。参考链接:Java> ArrayList的扩容因子为什么是1.5? - 明明1109 - 博客园

那为什么HashMap是2倍的扩容呢,上面刚说到1.5是最佳的扩容方式呀?因为他们不一样,HashMap是用hash值计算下标的,而2整数次幂可以方便的利用与(&)操作等价取模操作,提高效率,且2倍扩容后,下标不是原位置就是原下标 + 原数组长度。为什么加载因子是0.75呢?(说到这里,想一想,hashMap可以不扩容吗?可以呀,结构是数组加链表,不扩容,就把葡萄串一直王往下结呗)那为什么要扩容?降低hash冲突,提高时间效率;想到这就好理解了,为了提高效率要降低hash冲突,而hash冲突严重时,链表会转换为红黑树。转换为红黑树是无奈之举,为了提高查询效率,但是空间复杂度大大提高。因此希望尽量不要有红黑树<红黑树这个数据结构很厉害,先占个坑,好好学习后再写上一篇总结,目前我觉得最好的设计便是其动态规划思想,说起动态规划,回头还得补上一篇总结,这个是好东西,太多了先挖个坑>生成。而当负载因子为0.75时,当数据达到扩容要求时,map中有链表长度大于8(转为红黑树的临界值)的概率极小(概率上算(二项分布(泊松分布)可以算出来):0.00000006)。

参考链接:

ArrayList的扩容机制,扩容为原容量的1.5倍这种说法严谨吗?_ql_7256的博客-CSDN博客_arraylist扩容为什么是1.5倍

源码分析---HashMap中链表和红黑树的转换阈值 - 知乎

Java> ArrayList的扩容因子为什么是1.5? - 明明1109 - 博客园


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值