ArrayList的扩容机制

news/2025/2/21 7:57:54/

前置知识

ArrayList的底层实现是一个Object[],而LinkedList的底层实现是一个链表
ArrayList与LinkedList相对比:

  • ArrayList在随机访问时可以做到O(1),但是LinkedList的随机访问就是遍历链表,所以时间复杂度是O(N)
  • ArrayList在插入/删除元素时,需要移动额外的很多元素,但是LinkedList在插入/删除时无需移动其他元素,效率更高
  • 如果数据需要频繁的插入/删除,那么建议使用LinkedList
  • 如果数据较为稳定,希望高效的访问元素,建议使用ArrayList

利用反射获取ArrayList容量

由于ArrayList中并没有提供获取底层数组的方法,如何利用反射来获取ArrayList的真实容量

public static int getCapatity(ArrayList list) {  int len = 0;  try {  // 获取Class对象  Class<?> clazz = Class.forName("java.util.ArrayList");  // 获取类属性  Field field = clazz.getDeclaredField("elementData");  // 暴力打开权限  field.setAccessible(true);  Object[] objList = (Object[]) field.get(list);  // 返回数组长度  len = objList.length;  } catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) {  e.printStackTrace();  }  // 返回数组长度  return len;  
}

ArrayList的扩容机制

首先,创建ArrayList的方式有哪些
ArrayList提供了三个构造方法来创建ArrayList

// 1. 无参构造
public ArrayList() // 2. 指定容量大小
public ArrayList(int initialCapacity) // 3. 指定元素来创建  
public ArrayList(Collection<? extends E> c) 

无参构造

当调用无参构造来创建ArrayList时,看此无参构造方法的源码

public ArrayList() {  this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;  
}

其中elementData就是ArrayList实现的Object[ ],在无参构造中,直接为数据赋了一个默认的空元素值,也就是此时的容量为0
当ArrayList的容量不足时,扩容的机制是:
当目前的容量是0时,第一次扩容后的容量为10,以后每次扩容后的容量都是原始容量的1.5倍!!!
但是此处的1.5倍并不是在算术意义上的size*1.5,因为计算机并不能真正处理浮点数
此处是1.5倍是位运算的右移1位,能够避免浮点数的问题
即:

扩容后的容量 = 原始容量 + 原始容量 >> 1

当容量不足时,会创建一个新的指定大小的新数组,将原始数组中的内容拷贝到新的数组中(旧的数组不被再引用,就会被垃圾回收掉)
分析以下代码来试试:

// 1. 通过无参构造创建,此时容量为0
ArrayList list = new ArrayList();
// 2. 向容器中添加元素
list.add(5);
// 此时容量不足,触发第一次扩容,此时容量为10// 3. 继续添加10个元素,添加后共有11个元素
for(int i = 1; i <= 10 ;i ++){list.add(i);
}
// 当要插入第11个元素时,检查发现此时容量不足,触发第二次扩容
// 扩容后的容量为 = 10 + 10 >> 1 = 15;// 4. 此时有11个元素,容量为15
// 继续添加
for(int i = 1;i <= 10 ; i ++){list.add(i);
}
// 当添加第16个元素时,发现容量又不足了,所以此时触发第三次扩容
// 新的容量 = 15 + 15 >> 1 = 22

有参构造指定容量大小

来看利用ArrayList的指定容量大小的源码:如果指定的容量大于0,就会创建指定容量大小的数组。如果指定的容量小于0,抛出异常。

public ArrayList(int initialCapacity) {  if (initialCapacity > 0) {  this.elementData = new Object[initialCapacity];  } else if (initialCapacity == 0) {  this.elementData = EMPTY_ELEMENTDATA;  } else {  throw new IllegalArgumentException("Illegal Capacity: "+  initialCapacity);  }  
}

当指定了ArrayList的初始容量后,以后每次的扩容就是 扩容后的容量 = 原始容量 + 原始容量 >> 1,如果初始容量为0,还是会第一次扩容为10

指定元素创建

直接传入一个集合,来创建包含指定元素的ArrayList

ArrayList list = new ArrayList(Arrays.asList(1,2,3));

此时会使用指定的元素大小作为起始容量,以后的扩容规则还是同上。

addAll()

add()方法是一次添加一个元素,当使用addAll()方法可以一次添加多个元素。
以上的扩容机制都是在add()方法触发的情况下,新的容量 = 原始容量 + 原始容量 >> 1
当addAll()触发扩容时,扩容机制是不一样的。
会在 默认下次扩容后的容量大小当前所有的元素数量 之间选择一个较大值

ArrayList list = new ArrayList();
// 此时容量为0
list.add(Arrays.asList(1,2,3));
// 此时容量不足,触发扩容,
// 此时的元素数量是3,默认下次扩容后的容量是10
// 选择10 作为扩容后的容量

再来看一个例子

Array list = new ArrayList();
// 刚创建,此时容量为0
// 继续添加,容量不足,触发扩容
list.addAll(Arrays.asList(1,2,3,4,5,6,7,8,9,10,11));
// 默认下次扩容后的容量是10, 但是此时的元素数量是11,
// 所以扩容后的容量为11

再来看一个例子

ArrayList list = new ArrayList();
for(int i = 1;i <= 10 ;i ++){list.add(i);
}
// 此时容量为10,容量不足
list.addAll(Arrays.asList(1,2,3))
// 下次扩容后的容量为15,元素数量是13,
//  所以扩容后的容量是15

总结

三种创建ArrayList的方式:

  • ArrayList() 会创建一个起始容量为0的大小
  • ArrayList(int capacity)以指定的大小作为起始容量
  • ArrayList(Collection c)以指定的元素的数量作为起始容量

两种扩容机制:

  • add()如果原始容量是0,则扩容后的容量为10,以后每次扩容都是之前的1.5倍
  • addAll()会在下次扩容的大小和当前元素数量之间选择一个较大值

http://www.ppmy.cn/news/54113.html

相关文章

API 扫盲贴,8分钟快速搞懂 API 框架

API&#xff08;应用程序编程接口&#xff09;是一种传递信息和指令的工具&#xff0c;它通过不同的功能和协议等手段&#xff0c;允许不同的软件或系统之间进行通信和交互。作为程序员或开发人员&#xff0c;API 是你日常工作中必不可少的组成部分。在本文中&#xff0c;我们将…

Linux第三章

文章目录 前言一、Linux的root用户1.用户和用户组2.查看权限控制信息3.chmod命令4.chown命令 总结 前言 一、Linux的root用户 无论是Windows、MacOS、Linux均采用多用户的管理模式进行权限管理。在Linux系统中&#xff0c;拥有最大权限的账户名为&#xff1a;root&#xff08;…

换肤实现及LayoutInflater原理

文章目录 背景实现换肤步骤解析插件 apk 的包信息获取插件 apk 的 Resources 对象替换资源 简单的插件化换肤实现和存在的问题换肤如何动态刷新&#xff1f;控件换肤刷新的性能考虑如何降低 xml 布局中 View 的替换成本LayoutInflater 原理LayoutInflater.Factory2 替换 View 小…

【Linux基本指令和权限(1)】

本文思维导图&#xff1a; 文章目录 一、Linux操作的特点二、使用指令从Xhell登录云服务器三、基本指令1.ls指令2. pwd指令&#xff1a;3.cd指令4. touch指令5. rm指令 写在最后 Linux是一个操作系统&#xff0c;操作系统是一款做软硬件管理的软件。 一、Linux操作的特点 Li…

基于springboot+mysql+html实现智能停车场管理系统

基于springbootmysqlhtml实现智能停车场管理系统 一、系统介绍1、系统主要功能&#xff1a;2.涉及技术框架&#xff1a;3.本项目所用环境&#xff1a; 二、功能展示三、其它系统四、获取源码 一、系统介绍 1、系统主要功能&#xff1a; 系统管理&#xff1a;角色管理、接口管…

AODV路由算法在无线传感器网络中的设计与仿真(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 此代码用于MATLAB GUI&#xff0c;其中为WSN实现了AODV路由协议。源节点每次都会随着数据包的数量而变化。GUI的快照已附加。它…

AIhelp智能问答

前言 2023年,科技圈里,持续爆火的科技应用,毫无疑问是生成式AI,chatGPT了的,之所以令人惊叹,正是因为它的强大 可以这么认为,chatGPT能够解决很多问题,尤其是问答,问题答案的搜索,远比百度,google要精准,方便得多 如何提出高质量的问题,写好一个promot提示词,尤为重要,提出问题…

NOPI用法之自定义单元格背景色(3)

NPOI针对office2003使用HSSFWorkbook&#xff0c;对于offce2007及以上使用XSSFWorkbook&#xff1b;今天我以HSSFWorkbook自定义颜色为例说明&#xff0c;Office2007的未研究呢 在NPOI中默认的颜色类是HSSFColor&#xff0c;它内置的颜色有几十种供我们选择&#xff0c;如果不…