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 扩容机制的更多相关文章

  1. ArrayList扩容机制

    一.先从 ArrayList 的构造函数说起 ArrayList有三种方式来初始化,构造方法源码如下: 1 /** 2 * 默认初始容量大小 3 */ 4 private static final i ...

  2. ArrayList扩容机制以及底层实现

    简介 来源:博客园    作者:吾王彦 博客链接:https://www.cnblogs.com/qinjunlin/p/13724987.html ArrayList动态数组,是 java 中比较常 ...

  3. ArrayList扩容机制实探

    ArrayList初始化 问题:执行以下代码后,这个list的列表大小(size)和容量(capacity)分别是多大? List<String> list = new ArrayList ...

  4. 面试之arrayList扩容机制

    参考: ArrayList扩容详解_java编程艺术的博客-CSDN博客_arraylist扩容 ArrayList底层是数组elementData,用于存放插入的数据.初始大小是0,当有数据插入时, ...

  5. ArrayList源码解析(二)自动扩容机制与add操作

    本篇主要分析ArrayList的自动扩容机制,add和remove的相关方法. 作为一个list,add和remove操作自然是必须的. 前面说过,ArrayList底层是使用Object数组实现的. ...

  6. ArrayList的扩容机制

    一.ArrayList的扩容机制 1.扩容的计算方式是向右位移,即:newSize = this.size + (this.size>>1).向右位移,只有在当前值为偶数时,才是除以2:奇 ...

  7. 浅谈JAVA中HashMap、ArrayList、StringBuilder等的扩容机制

    JAVA中的部分需要扩容的内容总结如下:第一部分: HashMap<String, String> hmap=new HashMap<>(); HashSet<Strin ...

  8. 【数组】- ArrayList自动扩容机制

    不同的JDK版本的扩容机制可能有差异 实验环境:JDK1.8 扩容机制: 当向ArrayList中添加元素的时候,ArrayList如果要满足新元素的存储超过ArrayList存储新元素前的存储能力, ...

  9. 关于ArrayList的扩容机制

    关于ArrayList的扩容机制 ArrayList作为List接口常用的一个实现类,其底层数据接口由数组实现,可以保证O(1) 复杂度的随机查找, 在增删效率上不如LinkedList,但是在查询效 ...

  10. Java ArrayList自动扩容机制

    动态扩容 1.add(E e)方法中 ①  ensureCapacityInternal(size+1),确保内部容量,size是添加前数组内元素的数量 ②  elementData[size++] ...

随机推荐

  1. 四月二十七日java基础知识

    1.多线程的主要目的是讲一个程序中的各个"程序段"并发化2.并行执行通常表示为同一时刻有多个代码在处理器上执行3.并发执行通常表示为在单个处理器上,同一时刻只执行一个代码,但在一个 ...

  2. react18中antd的Upload组件上传头像,并且拿到服务器返回的头像的url地址在页面中显示头像

    业务需求:上传头像,上传完毕后拿到头像的url,把头像展示在页面中,最终把头像url和其他用户信息一起发送给服务器 上传头像流程 导入 Upload 组件和图标(一个加号,一个加载中) import ...

  3. 深度学习--魔法类nn.Module

    深度学习--魔法类nn.Module 作用 pytorch 封装了一些基本的网络类,可以直接调用 好处: 可以直接调用现有的类 容器机制:self.net = nn.Sequential() 参数返回 ...

  4. mysql 自动挂掉

    今天在看后台的时候,发现登录不上去了,登录页面是可以访问,但是就是登录不上去,上了后台看了一下,说mysql连接超时,然后我重启了一下服务器,发现依然报mysql的错误,我尝试连接mysql, 报了一 ...

  5. react 兄弟组件传值(发布订阅,使用于任何组件传值,包括vue)

    react中兄弟组件传值常规操作一般是,A组件传给父组件,父组件再传给B组件 非常规操作 利用  pubsub-js 在Home组件内调用 PubSub.publish("第一个参数是事件名 ...

  6. Centos7.x 更换Jenkins构建目录

    原由:最近因为原来的Jenkins构建目录,已经要满了,想着更换下构建目录,此篇文件简单介绍下更换过程. 注:此文章可能仅适用于我个人,仅供参考.如有其他办法,欢迎评论指教. 查了几种方法,最终选为使 ...

  7. 考前必备fa宝——对拍

    2022.11.24:晚上zxs学长发来了他的博客,所以我仿照写一篇. https://www.cnblogs.com/Dita/p/duipai.html 对拍 对拍这个东西,就是可以比较两份代码跑 ...

  8. SpringBoot 自动扫描第三方包及spring.factories失效的问题

    为什么会找不到 Spring 依赖注入 就是要让spring找到要注入的类 并且识别到了 @Component.@Service 等注解. 1. 当在开发的第三方包里写明了 @Component.@S ...

  9. 使用 shell 脚本自动申请进京证 (六环外) —— debug 过程

    问题现象 用 shell 脚本写了一个自动办理六环外进京证的工具 <使用 shell 脚本自动申请进京证 (六环外)>,然而运行这个脚本总是返回以下错误信息: { "msg&qu ...

  10. golang在编程语言排行榜上排名第10,请不要说golang已死。

    四月头条:编程语言 Zig 进入 TIOBE 指数前 50 名 最近,我们讨论了高性能编程语言的出现.由于需要处理的数据量越来越大,这些编程语言正在蓬勃发展.因此,C 和 C++ 在前十名中表现良好, ...