返回顶部
首页 > 资讯 > 后端开发 > JAVA >探秘ArrayList源码:Java动态数组的背后实现
  • 685
分享到

探秘ArrayList源码:Java动态数组的背后实现

java开发语言算法数据结构 2023-08-16 14:08:39 685人浏览 泡泡鱼
摘要

探秘ArrayList源码:Java动态数组的背后实现 一、成员变量二、构造器1、默认构造器2、带初始容量参数构造器3、指定collection元素参数构造器 三、add()方法扩容机制四、场景分析1、对于ensureExpli



一、成员变量

读者需先对源码的成员变量阅览一遍,看个眼熟,有助于后面源码的理解

private static final long serialVersionUID = 8683452581122892189L;        private static final int DEFAULT_CAPACITY = 10;        private static final Object[] EMPTY_ELEMENTDATA = {};     //用于默认大小空实例的共享空数组实例。      //我们把它从EMPTY_ELEMENTDATA数组中区分出来,以知道在添加第一个元素时容量需要增加多少。    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};        transient Object[] elementData;         private int size;

二、构造器

1、默认构造器

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

以无参数构造方法创建 ArrayList时,实际上初始化赋值的是一个空数组。此时并没有为它创建对象,当真正对数组进行添加元素操作时,才真正分配容量。即向数组中添加第一个元素时,数组容量扩为 10。

2、带初始容量参数构造器

public ArrayList(int initialCapacity) {    if (initialCapacity > 0) {//初始容量大于0        //创建initialCapacity大小的数组        this.elementData = new Object[initialCapacity];    } else if (initialCapacity == 0) {//初始容量等于0        //创建空数组        this.elementData = EMPTY_ELEMENTDATA;    } else {//初始容量小于0,抛出异常        throw new IllegalArgumentException("Illegal Capacity: "+               initialCapacity);    }}

3、指定collection元素参数构造器

 public ArrayList(Collection c) {    elementData = c.toArray();    if ((size = elementData.length) != 0) {        // c.toArray might (incorrectly) not return Object[] (see 6260652)        if (elementData.getClass() != Object[].class)            elementData = Arrays.copyOf(elementData, size, Object[].class);    } else {        // replace with empty array.        this.elementData = EMPTY_ELEMENTDATA;    }}

三、add()方法扩容机制

在进入ArrayList的核心源码扩容机制前,我们首先需要对源码中涉及到的一些变量进行一个初步的了解,这将有助于你对源码的深入了解

  1. minCapacity:数组所需的最小容量
  2. elementData:存储ArrayList元素的数组缓冲区

在这里插入图片描述

public boolean add(E e) {   ensureCapacityInternal(size + 1);  // size = 0   elementData[size++] = e;   return true;}
private void ensureCapacityInternal(int minCapacity) {//minCapacity = 1    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));//elementData = {}}
private static int calculateCapacity(Object[] elementData, int minCapacity) {    //判断elementData是否为空数组    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {//DEFAULTCAPACITY_EMPTY_ELEMENTDATA =  {}        return Math.max(DEFAULT_CAPACITY, minCapacity);//DEFAULT_CAPACITY = 10;minCapacity = 1    }    return minCapacity;}
private void ensureCapacityInternal(int minCapacity) {//minCapacity = 1    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));//ensureExplicitCapacity(10)}
private void ensureExplicitCapacity(int minCapacity) {//minCapacity = 10    modCount++;    if (minCapacity - elementData.length > 0)//elementData.length = 0        grow(minCapacity);}
private void grow(int minCapacity) {//minCapacity = 10    int oldCapacity = elementData.length;//oldCapacity = 0    int newCapacity = oldCapacity + (oldCapacity >> 1);//newCapacity  = 1.5*oldCapacity = 0    if (newCapacity - minCapacity < 0)//minCapacity = 10        newCapacity = minCapacity;//newCapacity = 10    if (newCapacity - MAX_ARRAY_SIZE > 0)        newCapacity = hugeCapacity(minCapacity);    //利用Arrays.copyOf()方法进行扩容    elementData = Arrays.copyOf(elementData, newCapacity);}
private static int hugeCapacity(int minCapacity) {     if (minCapacity < 0) // overflow         throw new OutOfMemoryError();     return (minCapacity > MAX_ARRAY_SIZE) ?         Integer.MAX_VALUE :         MAX_ARRAY_SIZE; }
  1. 增加一个新元素,此时所需的最小容量是 minCapacity = size+1
  2. 首先判断底层数组 elementData 是否是空数组
    • 如果是空数组,则更新 minCapacity 为默认容量10,
    • 如果不是空数组,则对 minCapacity 不做变更
  3. 判断所需最小容量 minCapacity 是否大于缓冲数组 elementData 的长度:
    • 如果大于,则进行扩容 grow()
    • 否则不做处理
  4. 扩容 grow()中,首先一上来就将 elementData 的长度扩长为原来长度的1.5倍,再对扩容后的 elementData 的长度和所需最小的容量 minCapacity进行判断
    • 如果扩容后的 elementData 的长度还小于 minCapacity 的长度,说明还是不够,此时就直接将minCapacity的长度赋值给elementData
    • 否则的话直接进行下一步即可
  5. 最后需要对 elementData 的长度进行一个是否超过最大限度值MAX_ARRAY_SIZE判断
    • 如果超过最大限度值,就看看所需的最小容量minCapacity是否大于最大限度值Integer.MAX_VALUE
    • 如果不是,就将数组的长度扩容为数组的最大限度值MAX_ARRAY_SIZE,如果是,则返回Integer.MAX_VALUE

四、场景分析

1、对于ensureExplicitCapacity()方法

1.1 add 进第 1 个元素到 ArrayList 时

当我们要 add 进第 1 个元素到 ArrayList 时,elementData.length 为 0 (因为还是一个空的 list),因为执行了 ensureCapacityInternal() 方法 ,所以 minCapacity 此时为 10。此时,minCapacity - elementData.length > 0成立,所以会进入 grow(minCapacity) 方法。

1.2 当 add 第 2 个元素时

当 add 第 2 个元素时,minCapacity 为 2,此时 elementData.length(容量)在添加第一个元素后扩容成 10 了。此时,minCapacity - elementData.length > 0 不成立,所以不会进入 (执行)grow(minCapacity) 方法。
添加第 3、4···到第 10 个元素时,依然不会执行 grow 方法,数组容量都为 10。

1.3 直到添加第 11 个元素

minCapacity(为 11)elementData.length(为 10)要大。进入 grow 方法进行扩容。

2、对于grow() 方法:

2.1 当 add 第 1 个元素时

oldCapacity 为 0,经比较后第一个 if 判断成立,newCapacity = minCapacity(为 10)。但是第二个 if 判断不会成立,即 newCapacity 不比 MAX_ARRAY_SIZE 大,则不会进入 hugeCapacity 方法。数组容量为 10,add 方法中 return true,size 增为 1。

2.2 当 add 第 11 个元素进入 grow 方法时

newCapacity 为 15,比 minCapacity(为 11)大,第一个 if 判断不成立。新容量没有大于数组最大 size,不会进入 hugeCapacity 方法。数组容量扩为 15,add 方法中 return true,size 增为 11。以此类推······

五、心得体会

  1. 变量 minCapacity 理解为我们添加元素进ArrayList集合中,底层数组所需的最小容量
  2. 变量 elementData.length 理解为我们添加元素进ArrayList集合中,底层数组的最大限度容量
  3. 当最小容量 minCapacity> 最大限度容量 elementData.length ,必定会触发扩容机制。
  4. 我们总是频繁地向ArrayList集合添加元素,开发人员也想到了这点,所以在ArrayList集合的扩容机制中,当我们添加第一个元素时,直接就把minCapacity设置为10,此处可以理解为,因为我们后续要频繁添加元素,为了不总是触发该集合的扩容机制,便“谎称”所需的最小容量是10,所以系统就直接把elementData.length设置为10

六、源码简易流程图

在这里插入图片描述

来源地址:https://blog.csdn.net/weixin_52533007/article/details/130638330

--结束END--

本文标题: 探秘ArrayList源码:Java动态数组的背后实现

本文链接: https://lsjlt.com/news/371324.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 探秘ArrayList源码:Java动态数组的背后实现
    探秘ArrayList源码:Java动态数组的背后实现 一、成员变量二、构造器1、默认构造器2、带初始容量参数构造器3、指定collection元素参数构造器 三、add()方法扩容机制四、场景分析1、对于ensureExpli...
    99+
    2023-08-16
    java 开发语言 算法 数据结构
  • 深入源码解析ArrayList:探秘Java动态数组的机制与性能
    文章目录 一、 简介ArrayList1.1 介绍ArrayList的基本概念和作用1.2 与数组的区别和优势 二、 内部实现2.1 数据结构:动态数组2.2 添加元素:add()方法的实现原理2.3 扩容机制:ensure...
    99+
    2023-12-22
    java 源代码管理
  • java实现动态数组
    本文实例为大家分享了java实现动态数组的具体代码,供大家参考,具体内容如下 数组最大的优点︰快速查询。scores[2]。数组最好应用于“索引有语意”的情况,但是如果索引比较长就...
    99+
    2024-04-02
  • Java 动态数组的实现示例
    目录静态数组动态数组的实现原理1.添加元素2.删除元素3.数组扩容4.数组缩减静态数组 Java中最基本的数组大家肯定不会陌生: int[] array = new int[6]...
    99+
    2024-04-02
  • java如何实现动态数组
    这篇文章主要介绍了java如何实现动态数组,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。具体内容如下数组最大的优点︰快速查询。scores[2]。数组最好应用于“索引有语意”...
    99+
    2023-06-20
  • Java注解实现动态数据源切换的实例代码
    当一个项目中有多个数据源(也可以是主从库)的时候,我们可以利用注解在mapper接口上标注数据源,从而来实现多个数据源在运行时的动态切换。实现原理在Spring 2.0.1中引入了AbstractRoutingDataSource, 该类充...
    99+
    2023-05-31
    java 数据源 切换
  • java实现数组的动态初始化
    一、什么是数组的初始化就是为数组开辟连续的内存空间,并为每个数组元素赋予值。二、如何对数组进行初始化1、动态初始化 只指定长度,由系统给出初始化值int[] arr = new int[5];推荐相关视频教程:java视频教程2、静态初始化...
    99+
    2019-02-20
    java 实现 数组 动态初始化
  • java中动态数组的具体实现
    声明:data为数组名。size为数组中最后一个元素的下一个位置。实现动态数组的原因:因为java中的数组是静态的,在new数组时就需要指定数组的大小,如果需要存储的元素为未知的个数,设置空间过大会造成浪费,设置空间过小会无法存入全部数据,...
    99+
    2019-01-23
    java教程 java 动态数组 实现
  • JAVA怎么实现PHP的动态数组
    本文操作环境:windows7系统、PHP7.1版、DELL G3电脑JAVA怎么实现PHP的动态数组?java实现php的数组:和java的Map类似用数字获取值:HashMap<Integer, Object> map = ...
    99+
    2018-07-18
    JAVA PHP
  • java动态数据源切换怎么实现
    在Java中实现动态数据源切换有多种方式,以下是其中一种常见的实现方法:1. 创建一个数据源容器类:创建一个类来管理多个数据源对象,...
    99+
    2023-10-09
    java
  • Java中数组容器(ArrayList)设的计与实现
    目录ArrayList为我们提供了哪些功能设计原理分析代码实现完整代码本篇文章主要跟大家介绍我们最常使用的一种容器ArrayList、Vector的原理,并且自己使用Java实现自己...
    99+
    2024-04-02
  • Android实现动态切换组件背景的方法
    本文所述的程序实现的功能为在软件中动态的选择组件背景,系统皮肤,自定义吐司背景等。 为实现这一要求,就需要用到安卓中的SharedPrefence的功能,首先在设置里面写一个控...
    99+
    2022-06-06
    方法 动态 Android
  • 【Java多数据源实现教程】实现动态数据源、多数据源切换方式
    前言 本文为 【Java多数据源实现教程】 相关知识,由于自己最近在做导师的项目的时候需要使用这种技术,于是自学了相关技术原理与实现,并将其整理如下,具体包含:多数据源的典型使用场景(包含业务复杂场景、读写分离场景),多数据源实现原理及实...
    99+
    2023-08-16
    java mybatis spring
  • Java数组怎么实现动态初始化
    这篇文章主要讲解了“Java数组怎么实现动态初始化”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java数组怎么实现动态初始化”吧!概念1、数组动态初始化只给定数组长度,系统默认初始化值。2...
    99+
    2023-06-30
  • SpringBoot通过源码探究静态资源的映射规则实现
    我们开发一个Spring Boot项目,肯定要导入许多的静态资源,比如css,js等文件 如果我们是一个web应用,我们的main下会有一个webapp,我们以前都是将所有的页面导在...
    99+
    2024-04-02
  • Java数组实现动态初始化的实例详解
    概念 1、数组动态初始化只给定数组长度,系统默认初始化值。 2、格式 数据类型[] 数组名 = new 数据类型[数组长度]; int[] arr = new int[3];...
    99+
    2024-04-02
  • java数组长度如何实现动态调整
    在Java中,数组的长度是固定的,一旦定义了数组的长度,就无法再进行动态调整。如果需要动态调整数组的长度,可以使用Java集合类中的...
    99+
    2023-10-26
    java
  • Java实现动态代理的实例代码
    目录前言静态代理 动态代理 CGLib实现动态代理 总结前言 动态代理在Java中有着广泛的应用,比如Spring AOP、Hibernate数据查询、测试框架的后端mock、RPC...
    99+
    2024-04-02
  • Vue动态组件表格的实现代码
    目录Vue组件数据源框架结构组件这里我自己封装了几个组件按钮组件图片组件滑动开关tap组件text组件Vue组件 数据源 //这里是HTML内容 这里通过下面的引入框架结构把数据源传...
    99+
    2022-11-13
    Vue动态组件 表格 Vue动态组件 Vue 表格
  • 揭秘 ASP 状态管理的秘密武器,轻松实现动态数据存储
    ASP.NET 的状态管理允许开发人员在一次请求和下一次请求之间存储和检索信息。这使得在应用程序的不同页面之间传递信息成为可能,而无需将其存储在数据库或其他持久性存储中。ASP.NET提供了几种不同的状态管理技术,包括: ViewSta...
    99+
    2024-02-24
    ASP.NET 状态管理 缓存 Session/cookies 应用 StateServer SQLServer/Oracle
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作