ArrayList扩容机制

简介: ArrayList的add方法添加元素时,先调用ensureCapacityInternal()确保容量。首次添加时,最小容量设为10,触发扩容;后续添加若超出当前容量,则调用grow()方法,将容量扩为原来的1.5倍。grow通过位移运算高效计算新容量,并复制元素到新数组。length是数组属性,length()是字符串方法,size()用于集合元素计数。

先来看Add方法
再来看看ensureCapacityInternal()方法,可以看到add()方法首先调用了ensureCapacityInternal(size+1)
当要add进第一个元素时,minCapacity为1,在Math.max()方法比较后,minCapacity为10
ensureExplicitCapacity()方法
我们来仔细分析一下
当我们要add进第一个元素到ArrayList时,elementData.length为0(因为还是一个空的list,里面还没有数据,所以没有进行扩容,默认扩容10),因为执行了ensureCapacityInternal()方法,所以minCapacity此时为10。此时,minCapacity - elemetData.length > 0(minCapacity=10,elemetData.length=0)成立,所以会进入==grow(minCapacity)==方法。
当add第2个元素时,minCapacity为2,此时elementData.length(容量)在添加第一个元素后扩容成10了。此时,minCapacity - elementData.length > 0不成立,所以不会进入(执行)==grow(minCapacity)==方法。
添加第3、4…到第10个元素时,依然不会执行==grow()==方法,数组容量都为10。
知道添加第11个元素,minCapacity(为11)比elementData.length(为10)要大。进行grow方法进行扩容
grow方法
Java
运行代码
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private void grow(int minCapacity) {
// oldCapacity为旧容量,newCapacity为新容量
int oldCapacity = elementData.length;//(0,10,15)
//将oldCapacity右移一位,其效果相当于oldCapacity/2;
// 我们知道位运算的速度远远快于整除运算,整句运算式的结果就是将新容量更新为旧容量的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 然后检查新容量是否大于最小需要容量,若还是小于最小需要容量,那么久把最小需要容量当作数组的新容量
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//判断新容量是否大于集合的最大容量(一般大不了)
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 给elementData从新赋值(10,15)
elementData = Arrays.copyOf(elementData, newCapacity);
}
int newCapacity = oldCapacity + (oldCapacity >> 1),所以 ArrayList 每次扩容之后容量都会变为原来的 1.5 倍!
“>>”(移位运算符):>>1 右移一位相当于除2,右移n位相当于除以 2 的 n 次方。这里 oldCapacity 明显右移了1位所以相当于oldCapacity /2。对于大数据的2进制运算,位移运算符比那些普通运算符的运算要快很多,因为程序仅仅移动一下而已,不去计算,这样提高了效率,节省了资源
通过例子探究一下grow()方法
当add第一个元素时,oldCapacity为0,经比较后第一个if判断成立,newCapacity = minCapacity(为10)。但是第二个if判断不会成立,即newCapacity不比MAX_ARRAY_SIZE大,则不会进入hugeCapacity方法。数组容量为10,add方法中return true,size增为1。
当add第11个元素进入grow方法时,newCapacity为15,比minCapacity(为11)大,第一个if判断不成立。新容量没有大于数组最大size,不会进入hugeCapacity方法。数组容量扩为15,add方法中rerurn,true,size增为11。
以此类推…
这里补充一点比较重要,但是容易被忽视掉的知识点:
java中的length属性是针对数组说的,比如说你声明了一个数组,想知道这个数组的长度则用到了length这个属性。
java中的length() 方法是针对字符串说的,如果想看这个字符串的长度则用到 length() 这个方法。
java中的size() 方法是针对泛型集合说的,如果想看这个泛型有多少元素,就调用此方法类查看!

相关文章
|
7月前
|
存储 人工智能 专有云
中智集团接入飞天企业版,行业云成果入选国资委AI标杆!
近日,中智集团接入阿里云飞天企业版打造的“中智行业云”成功入选国务院国资委首批“AI战略性高价值场景”。同时,该成果凭借先进的技术架构与场景赋能价值,还获评中国信息通信研究院“面向AI的智能化专有云底座”创新应用实践,成为AI与云计算在人力资源领域的标杆应用。
466 0
|
存储 安全 Java
BlockingQueue(阻塞队列)基本使用指南
BlockingQueue(阻塞队列)基本使用指南
674 1
|
5月前
|
安全 Java 数据处理
Java TLAB:JVM 多线程对象分配的无锁优化底层核心
TLAB(线程本地分配缓冲区)是JVM在Eden区为每线程私有分配的内存块,通过`top/end`指针实现无锁对象分配,彻底规避高并发下的竞态问题。它以极小内存浪费(&lt;1%)换取数十倍性能提升,是Java内存分配与GC优化的核心基石。(239字)
446 6
|
8月前
|
Java
ArrayList 的扩容机制解析
ArrayList扩容机制解析:添加元素时先检查容量,不足则触发扩容。默认初始容量为10,每次扩容1.5倍,通过数组拷贝实现,耗时O(n)。频繁扩容影响性能,建议预估容量并初始化指定大小,提升效率。
|
监控 Linux C++
【实战指南】4步实现C++插件化编程,轻松实现功能定制与扩展(2)
本文是《4步实现C++插件化编程》的延伸,重点介绍了新增的插件“热拔插”功能。通过`inotify`接口监控指定路径下的文件变动,结合`epoll`实现非阻塞监听,动态加载或卸载插件。核心设计包括`SprDirWatch`工具类封装`inotify`,以及`PluginManager`管理插件生命周期。验证部分展示了插件加载与卸载的日志及模块状态,确保功能稳定可靠。优化过程中解决了动态链接库句柄泄露问题,强调了采纳用户建议的重要性。
623 110
【实战指南】4步实现C++插件化编程,轻松实现功能定制与扩展(2)
|
设计模式 缓存 安全
Java设计模式的单例模式应用场景
Java设计模式的单例模式应用场景
455 4
|
算法 Java 数据库
理解CAS算法原理
CAS(Compare and Swap,比较并交换)是一种无锁算法,用于实现多线程环境下的原子操作。它通过比较内存中的值与预期值是否相同来决定是否进行更新。JDK 5引入了基于CAS的乐观锁机制,替代了传统的synchronized独占锁,提升了并发性能。然而,CAS存在ABA问题、循环时间长开销大和只能保证单个共享变量原子性等缺点。为解决这些问题,可以使用版本号机制、合并多个变量或引入pause指令优化CPU执行效率。CAS广泛应用于JDK的原子类中,如AtomicInteger.incrementAndGet(),利用底层Unsafe库实现高效的无锁自增操作。
767 0
理解CAS算法原理
|
安全 Java 应用服务中间件
JVM常见面试题(三):类加载器,双亲委派模型,类装载的执行过程
什么是类加载器,类加载器有哪些;什么是双亲委派模型,JVM为什么采用双亲委派机制,打破双亲委派机制;类装载的执行过程
566 35
JVM常见面试题(三):类加载器,双亲委派模型,类装载的执行过程
|
Java 编译器 UED
Arrays.asList() 数组转换成集合酿成的线上事故,差点要滚蛋了!
本文介绍了Java开发中使用`Arrays.asList()`方法将数组转换为集合时的一个常见陷阱。该方法返回的List是固定长度的,不支持添加或删除操作,直接使用可能导致线上故障。文章通过一次实际开发中的事故案例,分析了问题的原因,并提供了使用`java.util.ArrayList`进行封装的解决方案,以避免此类错误的发生。希望读者能从中吸取教训,提高代码的健壮性。
|
JavaScript 前端开发 程序员
Typescript 【实用教程】(2024最新版)含类型声明,类型断言,函数,接口,泛型等
Typescript 【实用教程】(2024最新版)含类型声明,类型断言,函数,接口,泛型等
571 0