ArrayList 扩容机制
ArrayList 基本介绍
ArrayList实现了List接口。它可以存储包括null的任何类型的对象,允许重复元素。ArrayList在内部使用一个数组来存储元素,当元素数量超过数组容量时,ArrayList会自动重新分配更大的内部数组,并且将现有元素复制到新数组中。ArrayList基本等同于Vector,但是ArrayList是线程不安全的(执行效率高),在多线程情况下不建议使用ArrayList。
ArrayList 源码阅读及操作机制
首先ArrayList中用来存储元素的数组是 Object 类型的数组 elementData,ArrayList的容量就是这个数组的大小。
transient Object[] elementData;
通过 debug 下面这段代码来观察ArrayList的扩容机制。
public class TestArrayList() {
public static void main(String[] args) {
ArrayList list = new ArrayList();
// ArrayList list = new ArrayList(4);
for (int i = 1; i <= 10; i++) {
list.add(i);
}
for (int i = 11; i <= 15; i++) {
list.add(i);
}
}
}
构造方法
当使用无参构造器创建ArrayList对象时,会创建一个默认容量为10的空数组。
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
// 用于默认大小的空实例的共享空数组。将其与 EMPTY_ELEMENTDATA 区分开,
// 以便在添加第一个元素时知道需要扩容多少。
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
使用指定大小的构造器创建ArrayList对象时,则初始 elementData 容量为指定大小。
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);
}
}
add(E e)
add(E e)方法将指定元素添加到列表末尾,该方法先调用ensureCapacityInternal()方法确保容量至少是所需的最小容量(即当前大小加一),然后再赋值。
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
由于ArrayList允许的元素类型是 Object,所以添加基本类型的数据时,会先将其转换为对应的包装类型。
ensureCapacityInternal(int minCapacity)
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
calculateCapacity()
该方法计算需要的容量,如果elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA(使用无参构造器时即符合该条件),则返回DEFAULT_CAPACITY(10),否则返回 minCapacity(size + 1)。
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private static final int DEFAULT_CAPACITY = 10;
ensureExplicitCapacity(int minCapacity)
modCount 记录列表被结构性修改的次数(结构性修改是指添加或删除一个或多个元素,或显式调整后备数组的大小,仅仅设置元素的值不是结构性修改)。如果需要的容量大于 elementData 的长度,则调用grow()方法进行扩容。
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
grow()
该方法增加容量以确保列表至少能够容纳最小容量参数minCapacity指定的元素数量。计算得到新容量应为旧容量的 1.5 倍。如果计算得到的新容量小于需要的最小容量,则新容量应为需要的最小容量(使用无参构造器第一次添加元素时即为这种情况,第一次扩容为 10)。如果请求的数组大小超过了虚拟机的限制,可能会导致 OutOfMemoryError。最后通过数组复制copyOf.copyOf()来进行真正的扩容。
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = copyOf.copyOf(elementData, newCapacity);
}
总结
当使用无参构造器创建ArrayList对象时,elementData 初始容量为 0,第一次添加元素时 elementData 扩容为 10,以后再需要扩容,按 1.5 倍来扩容。若使用有参构造器创建ArrayList对象,elementData 初始容量为指定大小,如果需要扩容,则扩容为 1.5 倍。
值得注意的是,由于每次调整容量都需要将所有元素复制到新数组中,所以在元素数量较多时,频繁地调整容量可能会导致性能下降。为了避免频繁地调整容量,可以使用ArrayList的指定大小的构造方法或在添加大量元素之前使用ensureCapacity()方法预先指定较大的容量,以减少容量调整的次数。
另外,当从ArrayList中删除元素时,并不会立即缩小内部数组的容量。如果希望减少内存占用,可以使用trimToSize()方法来调整ArrayList的容量,使其与元素数量匹配。这样可以释放未使用的内存空间。
ArrayList 扩容机制的更多相关文章
- ArrayList扩容机制
一.先从 ArrayList 的构造函数说起 ArrayList有三种方式来初始化,构造方法源码如下: 1 /** 2 * 默认初始容量大小 3 */ 4 private static final i ...
- ArrayList扩容机制以及底层实现
简介 来源:博客园 作者:吾王彦 博客链接:https://www.cnblogs.com/qinjunlin/p/13724987.html ArrayList动态数组,是 java 中比较常 ...
- ArrayList扩容机制实探
ArrayList初始化 问题:执行以下代码后,这个list的列表大小(size)和容量(capacity)分别是多大? List<String> list = new ArrayList ...
- 面试之arrayList扩容机制
参考: ArrayList扩容详解_java编程艺术的博客-CSDN博客_arraylist扩容 ArrayList底层是数组elementData,用于存放插入的数据.初始大小是0,当有数据插入时, ...
- ArrayList源码解析(二)自动扩容机制与add操作
本篇主要分析ArrayList的自动扩容机制,add和remove的相关方法. 作为一个list,add和remove操作自然是必须的. 前面说过,ArrayList底层是使用Object数组实现的. ...
- ArrayList的扩容机制
一.ArrayList的扩容机制 1.扩容的计算方式是向右位移,即:newSize = this.size + (this.size>>1).向右位移,只有在当前值为偶数时,才是除以2:奇 ...
- 浅谈JAVA中HashMap、ArrayList、StringBuilder等的扩容机制
JAVA中的部分需要扩容的内容总结如下:第一部分: HashMap<String, String> hmap=new HashMap<>(); HashSet<Strin ...
- 【数组】- ArrayList自动扩容机制
不同的JDK版本的扩容机制可能有差异 实验环境:JDK1.8 扩容机制: 当向ArrayList中添加元素的时候,ArrayList如果要满足新元素的存储超过ArrayList存储新元素前的存储能力, ...
- 关于ArrayList的扩容机制
关于ArrayList的扩容机制 ArrayList作为List接口常用的一个实现类,其底层数据接口由数组实现,可以保证O(1) 复杂度的随机查找, 在增删效率上不如LinkedList,但是在查询效 ...
- Java ArrayList自动扩容机制
动态扩容 1.add(E e)方法中 ① ensureCapacityInternal(size+1),确保内部容量,size是添加前数组内元素的数量 ② elementData[size++] ...
随机推荐
- [软件设计&系统建模] Web软件通用能力模块
0 基础工具 1 日志 2 权限 3 文件处理(下载/上传) 4 对象池 对象池 数据库连接池 线程池 5 微服务 服务网关 配置中心 注册中心 服务调用 服务熔断 健康检测 Actuator 6 缓 ...
- ModelAndView方法的返回值类型
一.ModelAndView @RequestMapping("/selectById") public ModelAndView queryById(Integer id){ M ...
- 实时分布式低延迟OLAP数据库Apache Pinot探索实操
@ 目录 概述 定义 特性 何时使用 部署 Local安装 快速启动 手动设置集群 Docker安装 快速启动 手动启动集群 Docker Compose 实操 批导入数据 流式导入数据 概述 定义 ...
- RDIFramework.NET WinForm版新增通知公告、系统新闻模块
1.系统新闻功能描述 系统新闻模块,用户可以根据实际情况做相应应用,如用在内部业务系统的展示中或网站上新闻的展示.新闻可以分类进行管理,非常的实用.系统新闻管理主要分为添加.修改.删除与移动系统新闻. ...
- AI人工智能简史
AI人工智能简史 最近学习AI,顺便整理了一份AI人工智能简史,大家参考: 1951年 第一台神经网络机,称为SNARC: 1956年 达特茅斯学院会议,正式确立了人工智能的研究领域: 1966年 M ...
- 设置Windows主机的浏览器为wls2的默认浏览器
这里以Chrome为例. 1. 准备工作 wsl是可以使用Windows主机上安装的exe程序,出于安全考虑,默认情况下改功能是无法使用.要使用的话,终端需要以管理员权限启动. 我这里以Windows ...
- 微服务为什么要用到 API 网关?
本文介绍了 API 网关日志的价值,并以知名网关 Apache APISIX 为例,展示如何集成 API 网关日志. 作者程小兰,API7.ai 技术工程师,Apache APISIX Contrib ...
- 基于 Github 平台的 .NET 开源项目模板. 嘎嘎实用!
简介 大家好,为了使开源项目的维护和管理更方便一些,出于个人需求写了一款开源项目的模板,该模板基于 Github 平台,并使用 .NET 来实现管道功能. 在接受过实战检验后, 于今天开源, 项目地址 ...
- C# 打开蓝牙设置界面
蓝牙设置相关界面,以下是通过C#方式打开的几个方式,记录一下 蓝牙设置界面 1.控制面板命令bthprops.cpl 可以用控制面板 control+bthprops.cpl,也可以直接bthprop ...
- Vite-WeGPT聊天AI实例|vue3+pinia仿ChatGPT聊天界面
基于vue3.x+vite4+pinia2仿chatgpt聊天模拟实例Vue3-WeGPT. 基于Vite4.x+Vue3+Pinia2+VEPlus+Vue3-Markdown等技术实现仿ChatG ...