JavaSE之ArrayList扩容原理分析

 更新时间:2026年03月24日 09:48:15   作者:阳光洒落大地  
这篇文章主要介绍了JavaSE之ArrayList扩容原理分析,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

ArrayList扩容原理

ArrayList底层数据结构是数组!!!

数组的特点:固定长度,顺序存储,有下标,可重复。

代码

源码

追进ArrayList源码

得到:

追入:DEFAULTCAPACITY_EMPTY_ELEMENTDATA

得到:-- 意思是:创建一个final修饰的Object类型的空的常量数组。

总结:

将空数组赋值给elementData这个属性。此时elementData数组是空的。DEFAULTCAPACITY_EMPTY_ELEMENTDATA数组也是空的。

第一次追进add源码

得到:

  • (E e):就是你传入的 "aa" 。
  • size:现在等于0。

追入:ensureCapacityInternal

得到:

minCapacity:就等于 (size + 1)。--现在等于1。

然后利用 if判断,前面提到过,elementData是DEFAULTCAPACITY_EMPTY_ELEMENTDATA赋值得到的,所以现在它们两个相等。

条件成立,就进入,Math.max(DEFAULT_CAPACITY, minCapacity)意思是取括号里的较大的值,咱们现在知道minCapacity的值是1,所以现在追进DEFAULT_CAPACITY的源码去看看:

DEFAULT_CAPACITY的值是10。

所以这行代码的意思是:

minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);

将10重新赋值给minCapacity。

此时 minCapacity:等于10。

继续执行下一条语句:

追入:ensureExplicitCapacity

此时 minCapacity =10。

第一条语句:modCount++;追进去看看:

发现 modCount =0。

然后判断minCapacity - elementData.length是否大于 0 。因为前面说过elementData数组是空的,所以 10 - 0是大于 0的。

条件成立,执行grow(minCapacity);

 追入:grow

注意:重点来了

此时 minCapacity = 10。

语句:int oldCapacity = elementData.length;

此时 oldCapacity = 0。

语句:int newCapacity = oldCapacity + (oldCapacity >> 1);

位运算符:>>意思是 / 2。 <<的意思是 * 2。

此时newCapacity = 0。

语句:if (newCapacity - minCapacity < 0)

newCapacity = minCapacity;

判断 0 - 10是否小于 0 。

条件成立,执行:newCapacity = minCapacity;

此时newCapacity = 10。

语句:if (newCapacity - MAX_ARRAY_SIZE > 0)

newCapacity = hugeCapacity(minCapacity);

判断 10 -MAX_ARRAY_SIZE是否大于 0。

MAX_ARRAY_SIZE = 2147483639。

条件不成立,不执行:newCapacity = hugeCapacity(minCapacity);

继续往下走。

语句:elementData = Arrays.copyOf(elementData, newCapacity);

新数组 = Arrays.copyOf(旧的数组,新数组的长度):意思是复制数组,将旧数组复制到新的数组。

此时elementData数组长度为 10。

然后返回到二、第一次追进add源码执行下一条语句

elementData[size++] = e;
  • 前面说过size = 0;e = "aa";
  • 所以:elementData[0] = "aa";
  • 然后size++
  • 此时 size = 1。

第一遍追进之后数据的改变

  • size = 1。
  • elementData数组长度为 10。
  • newCapacity = 10。
  • modCount = 1。

第二次追进add源码

得到:

此时size = 1。

追入:ensureCapacityInternal

此时minCapacity =size + 1。

minCapacity = 2。

判断 elementData 和DEFAULTCAPACITY_EMPTY_ELEMENTDATA是否相等,因为经过第一次赋值导致 elementData已经是 10了。所以条件不成立,不执行里面的语句。

继续执行下一条语句。

追入:ensureExplicitCapacity

得到:

此时modCount = 1。

判断 minCapacity -elementData.length是否大于 0 。

因为此时elementData数组长度为 10。所以条件不成立,不执行里面语句。

所以此时返回到三、第二次追进add源码执行下一条语句。

elementData[size++] = e;

前面说过size = 1;e = "bb";

  • 所以:elementData[1] = "bb";
  • 然后size++
  • 此时 size = 2。

第二遍追进之后数据的改变

  • size = 2。
  • elementData数组长度为 10。
  • newCapacity = 10。
  • modCount = 2。

第十遍追进之后数据的改变

size = 10。

elementData数组长度为 10。

newCapacity = 10。

modCount = 10。

第十一次追进add源码

得到:

此时size = 10 。

追入:ensureCapacityInternal

  • 此时minCapacity =size + 1 。
  • minCapacity = 11 。
  • 判断 elementData 和DEFAULTCAPACITY_EMPTY_ELEMENTDATA是否相等,因为经过前面赋值导致 elementData已经是 10了。所以条件不成立,不执行里面的语句。
  • 继续执行下一条语句。

追入:ensureExplicitCapacity

  • 此时modCount = 10。
  • 判断 minCapacity -elementData.length是否大于 0 。
  • 因为 minCapacity = 11 。elementData数组长度为 10 。所以条件成立,执行里面的语句。

追入:grow

重点:

此时minCapacity = 11 。elementData.length = 10 。

语句:int oldCapacity = elementData.length;

此时oldCapacity = 10 。

语句:int newCapacity = oldCapacity + (oldCapacity >> 1);

意思是:newCapacity = 10 + (10除以 2)

此时newCapacity = 15 。

语句:if (newCapacity - minCapacity < 0)

newCapacity = minCapacity;

判断 15 - 11是否小于 0 。

不小于,所以不执行里面的语句:newCapacity = minCapacity;

语句:if (newCapacity - MAX_ARRAY_SIZE > 0)

newCapacity = hugeCapacity(minCapacity);

判断 15-MAX_ARRAY_SIZE是否大于 0。

MAX_ARRAY_SIZE = 2147483639。

条件不成立,不执行:newCapacity = hugeCapacity(minCapacity);

继续往下走。

语句:elementData = Arrays.copyOf(elementData, newCapacity);

意思是:新数组 =Arrays.copyOf(老数组,新数组长度);

elementData = Arrays.copyOf(elementData,15);

所以这是后 elementData 数组长度为 15 。

然后返回到四、第十一次追进add源码执行下一条语句

elementData[size++] = e;

所以:elementData[10] = "第十一次";

然后size++

此时 size = 11。

总结

1、底层创建了一个 Object[]的数组。数组名:elementData。此数组中没有元素。

2、通过List.add 调用 grow()扩容方法,数组长度变为10。

3、在数组存满之前 List.add中不会再调用grow()扩容方法了。

4、当第十一次存入时,List.add再次调用grow()扩容方法。

数组长度会变为原数组长度的1.5倍。

5、扩容不是在老数组基础上拼接的,而是创建了一个1.5倍长度的新数组。

并把老数组的元素复制到新数组。

面试时参考话术

ArrayList底层数据结构是数组,当创建ArrayList对象时,底层初始化了一个空数组,数组是Object类型,数组名是elementData。

当第一次添加元素时,数组长度扩容为10。

……

当第11次添加时,会触发扩容机制,其实就是调用 grow方法,扩容为原数组长度的1.5倍。

每次扩容时,都是创建一个新数组,将老数组的元素通过 Arrays工具类复制到新数组中。elementData 指向了新数组。

ArrayList和 LinkedList 区别?

ArrayList 底层数据结构 数组。

LinkedList 底层数据结构 链表。

功能上区别:

ArrayList 查询快,增删慢。

原因:顺序存储,有索引,可以根据索引,直接定位到元素,所以查询快;由于是顺序存储,新增或者删除,都会对后续的元素有影响。

LinkedList 查询慢,增删快。

原因:不是顺序存储,每个结点相连,一个结点中可以存储下一个和上一个结点,这样的话,增删元素,只对相邻的结点有影响,其他结点不受影响;由于没有下标,所以,查询元素时,需要(从头结点或尾结点)遍历。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Java多线程模拟银行系统存钱问题详解

    Java多线程模拟银行系统存钱问题详解

    本文将利用Java多线程模拟一个简单的银行系统,使用两个不同的线程向同一个账户存钱。文中的示例代码讲解详细,感兴趣的可以了解一下
    2022-09-09
  • java并发编程专题(十)----(JUC原子类)基本类型详解

    java并发编程专题(十)----(JUC原子类)基本类型详解

    这篇文章主要介绍了java JUC原子类基本类型详解的相关资料,文中示例代码非常详细,帮助大家更好的理解和学习,感兴趣的朋友可以了解下
    2020-07-07
  • java多线程之铁路售票系统

    java多线程之铁路售票系统

    这篇文章主要为大家详细介绍了java多线程之铁路售票系统,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-07-07
  • Apache Commons BeanUtils: JavaBean操作方法

    Apache Commons BeanUtils: JavaBean操作方法

    这篇文章主要介绍了Apache Commons BeanUtils: JavaBean操作的艺术,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-12-12
  • Java实现微信支付的项目实践

    Java实现微信支付的项目实践

    最近的一个项目中涉及到了支付业务,其中用到了微信支付和支付宝支付,本文就来介绍一下Java实现微信支付的项目实践,具有一定的参考价值,感兴趣的可以了解一下
    2023-10-10
  • @RequestParam注解加与不加有什么区别

    @RequestParam注解加与不加有什么区别

    这篇文章主要介绍了@RequestParam注解加与不加有什么区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-11-11
  • freemarker简介_动力节点Java学院整理

    freemarker简介_动力节点Java学院整理

    FreeMarker是一个模板引擎,一个基于模板生成文本输出的通用工具,使用纯Java编写,有兴趣的可以了解一下
    2017-08-08
  • SpringBoot实现统一日志上下文的实战指南

    SpringBoot实现统一日志上下文的实战指南

    这篇文章主要为大家详细介绍了SpringBoot实现统一日志上下文的相关方法,文中的示例代码讲解详细,具有一定的借鉴价值,感兴趣的小伙伴可以了解下
    2026-01-01
  • 解决openfeign调用时content-type的问题

    解决openfeign调用时content-type的问题

    文章主要描述了在使用OpenFeign调用其他服务时遇到的偶发性报错问题,问题原因在于请求头中Content-Type的不一致,通过修改OpenFeign的配置类或调整服务引用的配置类数量,解决了这个问题,同时,还讨论了当B服务接口也是文件上传接口时的解决方案
    2026-01-01
  • JAVA面试篇之逻辑运算符与按位运算符的区别详析

    JAVA面试篇之逻辑运算符与按位运算符的区别详析

    运算符在Java编程中至关重要的作用不容忽视,它们广泛应用于数学函数、赋值语句以及逻辑比较等多个方面,这篇文章主要介绍了JAVA面试篇之逻辑运算符与按位运算符区别的相关资料,需要的朋友可以参考下
    2025-08-08

最新评论