ArrayList源码分析之 add 方法

Clock Gating设计陷阱:为什么你的ICG cell总是导致setup violation?(附解决方案) 本文深入分析了数字IC设计中ICG(Integrated Clock Gating)单元导致setup时序违规的常见问题,包括时钟偏斜和数据路径过长两大核心原因。通过量化分析影响因素,提供了逻辑重组、寄存器重定时等优化策略,并介绍了预防性设计方法和物理实现阶段的优化技巧,帮助工程师有效解决ICG相关的setup violation问题。 阅读详情

在Java编程中,常常需要集中存放多个数据,从传统意义上讲,数组是我们的一个很好的选择,前提是我们事先已经明确知道我们将要保存的对象的数量。一旦在数组初始化时指定了这个数组长度,这个数组长度就是不可变的,如果我们需要保存一个可以动态增长的数据(在编译时无法确定具体的数量),

List 这个集合类是便为我们提供了相当于动态数组的功能。这个类中add方法尤为重要。

 1 目标

本次源码分析的目标是深入了解 List类中 add 方法的实现机制。

 

2 分析方法

首先编写测试代码,然后利用 Intellij Idea 的单步调试功能,逐步的分析其实现思路。

 

测试代码如下:

List<String> mList=new ArrayList<String>(); 
mList.add("张三");//断点
mList.add("李四");
mList.add("王五");

mList.add(1,"赵六");//断点

3 分析流程

点击调试按钮,开始分析流程。

 

3.1 构造函数

首先进行的是构造函数的分析,点击 Shift+F7进入构造函数实现。

 

/**
 * Constructs an empty list with an initial capacity of ten.
 */
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

点击进入后,我们看到的是以上代码,这里疑惑就来了,这个elemendata代表什么呢?这个DEFAULTCAPACITY_EMPTY_ELEMENTDATA又是什么意思呢?

 

我们可以在定位在elemenData处按 Ctrl+B进入查看做进一步分析。

transient Object[] elementData;

通过上述代码我们知道,elemenData是一个Object型的数组。那为什么会定义成Object型的呢?我们可以想一下,如果是String,int或是其他类型的,那么存放的数据是不是就受到了限制了呢?而Object型的就能保证我们可以将任何类型的数据存放进去。可以看到,Object前面有一个transient,那这个transient是什么意思呢?

我们都知道一个对象只要实现了Serilizable接口,这个对象就可以被序列化,java的这种序列化模式为开发者提供了很多便利,我们可以不必关系具体序列化的过程,只要这个类实现了Serilizable接口,这个类的所有属性和方法都会自动序列化。然而在实际开发过程中,我们常常会遇到这样的问题,这个类的有些属性需要序列化,而其他属性不需要被序列化,打个比方,如果一个用户有一些敏感信息(如密码,银行卡号等),为了安全起见,不希望在网络操作(主要涉及到序列化操作,本地序列化缓存也适用)中被传输,这些信息对应的变量就可以加上transient关键字。换句话说,这个字段的生命周期仅存于调用者的内存中而不会写到磁盘里持久化。

同理我们来看DEFAULTCAPACITY_EMPTY_ELEMENTDATA:

/**
 * Shared empty array instance used for default sized empty instances. We
 * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
 * first element is added.
 */
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

可见这是一个Object型的常量数组,并将其赋了空值。到这里我们就能知道构造方法里是将elemenData数组初始化为空。

 

3.2 booleanadd(E  e)方法  

 

接下来我们分析add( E  e)方法的实现机制。

/**
 * Appends the specified element to the end of this list.
 *
 * @param e element to be appended to this list
 * @return <tt>true</tt> (as specified by {@link Collection#add})
 */
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}
确定内部容量是否够了,size是数组中数据的个数,因为要添加一个元素,所以size+1,先判断size+1的这个个数数组能否放得下,就在这个方法中去判断数组.length是否够用了。

从上述代码可以看到,这里调用了ensureCapacityInternal方法,我们来看看其实现机制

private void ensureCapacityInternal(int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    ensureExplicitCapacity(minCapacity);
}

由此我们可以看出这是一个确定内部容量的方法,这里我们先定位在DEFAULT_CAPACITY上Ctrl+B进去可以看到其值为10;首先判断初始化的elementData是不是空的数组,也就是没有长度因为如果是空的话,minCapacity=size+1;其实就是等于1,空的数组没有长度就存放不了,所以就将minCapacity变成10,也就是默认大小,但是在这里,还没有真正的初始化这个elementData的大小。

我们继续按F7调试下一步,将会运行ensureExplicitCapacity方法
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}
minCapacity如果大于了实际elementData的长度,那么就说明elementData数组的长度不够用,不够用那么就要增加elementData的length。有的同学可能会疑惑minCapacity到底是什么呢?我们来分析一下:
1)           由于elementData初始化时是空的数组,那么第一次add的时候,minCapacity=size+1;也就minCapacity=1,在上一个方法(确定内部容量ensureCapacityInternal)就会判断出是空的数组,就会将minCapacity=10,到这一步为止,还没有改变elementData的大小。
2)           elementData不是空的数组了,那么在add的时候,minCapacity=size+1;也就是minCapacity代表着elementData中增加之后的实际数据个数,拿着它判断elementData的length是否够用,如果length不够用,那么肯定要扩大容量,不然增加的这个元素就会溢出。

F7调试下一步执行grow方法,arrayList核心的方法,能扩展数组大小的真正秘密。我们下一节详细分析该方法。

3.3 grow(int minCapacity)方法

 

按住 Shift+F7进入该方法的实现代码。

 


 
/**
 * Increases the capacity to ensure that it can hold at least the
 * number of elements specified by the minimum capacity argument.
 *
 * @param minCapacity the desired minimum capacity
 */
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length; //将扩充前的elemenData大小给oldCapacity
    int newCapacity = oldCapacity + (oldCapacity >> 1);//newCapacity的大小就是1.5倍的oldCapacity,相当于将elemenData之前的大小扩充为原来的1.5倍。
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
//这段代码判断将elemenData大小扩充1.5倍过后与minCapacity作比较,在§3.2节我们已经知道minCapacity默认值为10。判断成立便将minCapacity赋值给newCapacity,在这里就是真正的初始化elemenData的大小了。这段代码适应于elemenData数组为空的时候,elementData.length=0,则oldCapacity=0,newCapacity=0,所以该判断成立。
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
}minCapacity作比较,在§3.2节我们已经知道minCapacity默认值为10。判断成立便将minCapacity赋值给newCapacity,在这里就是真正的初始化elemenData的大小了。这段代码适应于elemenData数组为空的时候,elementData.length=0,则oldCapacity=0,newCapacity=0,所以该判断成立。
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
}

 

 

 


if (newCapacity- minCapacity < 0)
       
newCapacity = minCapacity;
 

 

if (newCapacity- MAX_ARRAY_SIZE > 0)
       
newCapacity = hugeCapacity(minCapacity);
//这里有个MAX_ARRAY_SIZE,可是MAX_ARRAY_SIZE代表什么呢?

按住Ctrl+B查看源码:

/**
 * The maximum size of array to allocate.
 * Some VMs reserve some header words in an array.
 * Attempts to allocate larger arrays may result in
 * OutOfMemoryError: Requested array size exceeds VM limit
 */
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

根据注释我们了解到MAX_ARRAY_SIZE代表最大容量限制。所以代码含义是如果newCapacity超过了最大容量限制的话就调用hugeCapacity方法,将能给的最大值给newCapacity。hugeCapacity方法又是怎么一回事呢?我们下一节再来介绍。

 

elementData= Arrays.copyOf(elementData,newCapacity);//最后copy数组,改变数组容量。

3.4 hugeCapacity()方法

 

按住 Shift+F7进入该方法的实现。

private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
}

 

从上述代码可以看出,hugeCapacity方法用于确定elemenData数组的最大容量。如果minCapacity > MAX_ARRAY_SIZE说明扩容时扩得过大,则返回Integer.MAX_VALUE,否则返回MAX_ARRAY_SIZE。在上一节已经说过MAX_ARRAY_SIZE的含义,那MAX_ARRAY_SIZE和Integer.MAX_VALUE的值具体又是多少呢?

按住Ctrl+B查看源码:

@Native public static final int   MAX_VALUE = 0x7fffffff;

 

3.5void add(int,E)方法

 

按住 Shift+F7进入该方法的实现代码:

/**
 * Inserts the specified element at the specified position in this
 * list. Shifts the element currently at that position (if any) and
 * any subsequent elements to the right (adds one to their indices).
 *
 * @param index index at which the specified element is to be inserted
 * @param element element to be inserted
 * @throws IndexOutOfBoundsException {@inheritDoc}
 */
public void add(int index, E element) {
    rangeCheckForAdd(index);

    ensureCapacityInternal(size + 1);  // Increments modCount!!
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
    elementData[index] = element;
    size++;
}

 

此方法用于在特定位置将元素添加进集合,也就是插入元素。先调用rangeCheckForAdd方法检查插入的位置是否合理,然后调用ensureCapacityInternal方法将index之后的元素都往后移动一位(具体分析看上面的小节),并在目标位置上将要插入的元素存放进elemenData数组。最后数组大小加一。

 

4 总结

本文分析了List类的 add 方法,包括在末尾添加元素和在指定位置添加元素。通过分析我们知道List集合就相当于一个elemenData数组,该数组可以实现自动扩容。当我们调用add方法的时候实际上的函数调用顺序为:

后面两个方法并不是每次调用add方法时,当添加进的元素数量大于elemenData默认容量时会调用grow方法进行扩容,而当扩容过大时会调用hugeCapacity方法进行最大容量限制。

 对比一下传统的数组,在使用数组时我们需要事先固定好其大小,然而在实际开发中我们往往是不能预测究竟会存放多少数据进去的,当我们需要改变数组大小时,又要回过头开修改代码,这岂不是徒增麻烦。而List集合就避免了这种麻烦,给开发人员与维护人员提供了极大地便利,所以在实际开发中被人们广泛使用。了解其实现机制是很有意义的。

 

YOLOv11改进策略【SPPF】| 将特征金字塔池化修改为:SPPCSPC,提升模型的特征提取能力和计算效率。 本文记录的是。YOLOv7中的SPPCSPC(Spatial Pyramid Pooling Cross Stage Partial Connections)模块是一种结合了(SPP)和(CSP)的改进结构,本文将其添加到YOLOv11中, 阅读详情

相关推荐

Android 音频源码分析——AudioTrack设备选择

Android 音频源码分析——AndroidRecord录音(一) Android 音频源码分析——AndroidRecord录音(二) Android 音频源码分析——AndroidRecord音频数据传输流程 Android 音频源码分析——audioserver启动 Android 音频源码分析——AudioFlinger Android 音频源码分析——AudioTrack设备选择 基于Andorid9.0源码 以AudioTrack为例,梳理下输出设备选择流程。 音频设备选择的影响因素: Au

奋斗的菜鸟ing 3971

数据结构思维 第二章 算法分析

第二章 算法分析 原文:Chapter 2 Analysis of Algorithms 译者:飞龙 协议:CC BY-NC-SA 4.0 自豪地采用谷歌翻译 我们在前面的章节中看到,Java 提供了两种实现List的接口,ArrayList和LinkedList。对于一些应用,LinkedList更快;对于其他应用,ArrayList更快。要确定对于特定的应用,

龙哥盟 4万+

别再只会用555了!手把手教你用运放和RC电路搭一个50Hz正弦波信号源(附Multisim仿真文件)

本文详细介绍了如何使用运放LM358和RC电路搭建一个精准的50Hz正弦波信号源,涵盖文氏电桥核心原理、单电源供电解决方案、Multisim仿真及实物调试技巧。通过实战指南和优化建议,帮助电子爱好者快速掌握低频信号生成技术,适用于各种测试和校准场景。

weixin_34116482的博客 364

数据结构思维 第三章 `ArrayList`

第三章 ArrayList 原文:Chapter 3 ArrayList 译者:飞龙 协议:CC BY-NC-SA 4.0 自豪地采用谷歌翻译 本章一举两得:我展示了上一个练习的解法,并展示了一种使用摊销分析来划分算法的方法。3.1 划分MyArrayList的方法对于许多方法,我们不能通过测试代码来确定增长级别。例如,这里是MyArrayList的get的实现:

龙哥盟 4万+

别再硬啃C代码了!用Simulink的Matlab Function模块手把手教你搭建CRC8校验模型(附完整M脚本)

本文详细介绍了如何利用Simulink的Matlab Function模块轻松实现CRC8校验模型,替代传统的C代码编写。通过可视化建模和即时验证,开发者可以高效完成算法设计,并自动生成嵌入式代码。文章包含完整的M脚本示例和工程实践技巧,特别适合嵌入式系统开发人员提升工作效率。

weixin_42520661的博客 183

星座SAR动目标检测(GMTI)

参考代码 星座SAR动目标检测 www.youwenfan.com/contentcsg/52399.html。

feifeigo123的博客 549

java数组解析_Java - 数组解析

一、什么是数组数组?什么是数组?在我印象中的数组是应该这样的:通过new关键字创建并组装他们,通过使用整形索引值访问它的元素,并且它的尺寸是不可变的!但是这只是数组的最表面的东西!深一点?就是这样:数组是一个简单的复合数据类型,它是一系列有序数据的集合,它当中的每一个数据都具有相同的数据类型,我们通过数组名加上一个不会越界下标值来唯一确定数组中的元素。不管在其他语言中数组是什么,在java中它就是...

weixin_31534091的博客 709

PL端:HDMI 输出实验

开发板没有HDMI编码芯片,是将FPGA的3.3差分IO直接连接到HDMI连接器,FPGA 完成 24 位 RGB 编码输出TMDS 差分信号。1、学会使用第三点IP核,首先是添加,后面的使用和自带IP核操作一致。2、主要还是在梳理vivado使用流程。

qq_43441284的博客 4287

【Java】ArrayList实现原理浅析,Java框架体系架构的知识

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } 2.1.2 指定数组容量大小参数的构造函数 指定容量>0,直接new一个指定大小的对象数组; =0,指定一个空对象数组 <0,抛异常 private static final Object[] EMPTY_

澎湖Java架构师的博客 374

03 Aras Innovator二次开发-服务端方法(二)

Aras Innovator二次开发-服务端方法书写

企业信息化(PLM,ERP.MES) 2016

基于STM32L4XX、HAL库的YT8531C-CA以太网收发器(PHY)驱动程序设计

是一款由裕太微推出的高性价比、低功耗的单端口 10/100M 以太网物理层收发器(PHY)。它通过标准的 MII(介质独立接口)或 RMII(精简介质独立接口)与微控制器的 MAC 层连接,实现将数字信号与网络模拟信号之间的转换。支持自动协商(Auto-Negotiation),自动选择最高速率和双工模式。支持 HP Auto-MDIX(自动交叉直连),无需区分直通线或交叉线。低功耗设计,特别适合电池供电或对功耗敏感的 STM32L4 应用。支持 MII 和 RMII 接口,节省引脚(RMII模式)。

zhusl6688的博客 628

JDK类库源码分析系列3--集合类分析(1) List集合3-ArrayList

一、ArrayList的结构 public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable public interface RandomAccess { } ​ 其继承的AbstractList,同时又实现RandomAccess这个算法标记接口。 二、成员变量 1、DEFAULT_

Feverasa的博客 340

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

ArrayList中add方法源码分析~

首先要明确ArrayList的底层实现是数组利用空参创建的集合时,在底层创建一个默认长度为0的数组添加第一个元素时,底层会创建一个新的长度为10的数组存满时,会扩容1.5倍如果一次添加多个元素,1.5倍还放不下,则新创建数组的长度以实际为准。

2501_90392153的博客 1904

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

java8 ArrayList源码分析1->add方法和remove方法

今天在做算法题目的时候,使用到了ArrayList,在获取size想到了一个问题,在使用add操作的时候,ArrayList的源码是怎么实现的,然后我下载 了jdk1.8的源码。 首先,ArrayList的初始容量是10,无论是什么操作,首先会调用最低容量为10。最终的实现是使用了System.arrayopy方法。 如果执行add操作 ,有两种方法可供选择: 1.add(E e)方法,该方法会在ArrayList的尾部插入元素,首先会调用ensureCapacityInternal方法来检查数组的容

FanJqi的博客 596

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

ArrayList源码分析之add 方法

欢迎点击「算法与编程之美」↑关注我们!本文首发于微信公众号:"算法与编程之美",欢迎关注,及时了解更多此系列博客。在Java编程中,常常需要集中存放多个数据,从传统意...

算法与编程之美 837

ArrayList_add( ) 方法源码分析

以下为ArrayList的基础参数: /** * 默认初始容量 */ private static final int DEFAULT_CAPACITY = 10; /** * 用于空实例的共享空数组实例 */ private static final Object[] EMPTY_ELEMENTDATA = {}; /** * 数组缓冲区中存储ArrayList元素的数组缓冲区...

butterBallj的博客 420

ArrayList中add方法源码分析

当我们通过ArrayList的无参构造方法创建一个ArrayList对象时,ArrayList的无参构造方法会初始化一个数组类型的elementData成员变量,该成员变量就是ArrayList具体用来存放数据的变量,由此可知ArrayList是基于数组来实现的。 当程序调用add方法向我们新建的集合类添加值的时候,添加方法会先调用ensureCapacityInternal方法,对集合容量进行确认。 ensureCapacityInternal方法用来对ArrayList进行扩...

bc_Reversing的博客 335

ArrayList底层源码分析之list.add方法

list.add方法源码分析 前言:本人第一次写文章,如果文章中有地方表达的不够正确或不清楚请指出来,我会加以改正。还请多多包含。 正文:相信在日常撸代码的过程中大家用到ArrayList集合中的场景不算少数,今天我也是突发奇想,想看下ArrayList是怎么实现的。这篇文章主要讲它的add()方法,在这之前先说明一下ArrayList中add()方法内使用到的属性,方便大家对add()方法的理解...

Mr_Cat_的博客 1221

源码分析——ArrayList的add,get,retainAll方法原理分析

ArrayList的add方法 /** * Shared empty array instance used for default sized empty instances. We * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when * first el...

随手记 1140

ArrayList构造函数和add方法源码分析

ArrayList构造器 /** * 初始容量大小 */ private static final int DEFAULT_CAPACITY = 10; private static final Object[] EMPTY_ELEMENTDATA = {}; private static final Object[] DEFAULTCAPACITY_EMPTY_E...

yjh1271845364的博客 501

ArrayList源码分析(源码只备注add方法过程)

package com.lidl.com.lidl.web; import java.util.*; public class ArrayListTest { public static void main(String[] args) { List<String> list = new ArrayList<>(); list...

李度的博客 335

ArrayList的add()方法扩容原理,源码分析

ArrayList的add()方法扩容原理,源码分析

java架构师分享,有问题可以联系我 178

ArrayList的add()方法源码分析(jdk1.8)

成员变量 //默认的初试容量 private static final int DEFAULT_CAPACITY = 10; // 当你造了一个指定长度为0的数组,就会把elementData实例化为这个长度为0的数组 private static final Object[] EMPTY_ELEMENTDATA = {};、 // 当你造一个默认长度ArrayList时,就会把emementData实例化为这个长度为0的数组 private static final Object[] DEFAULT.

qq_50748627的博客 696

java集合 arrayList源码分析add方法自动扩容机制(上)

一、ArrayList简介 1.1、ArrayList概述 1)ArrayList是可以动态增长和缩减的索引序列,它是基于数组实现的List类。 2)该类封装了一个动态再分配的Object[]数组,每一个类对象都有一个capacity属性,表示它们所封装的Object[]数组的长度,当向ArrayList中添加元素时,该属性值会自动增加。  3)ArrayList的用法和Vector向类似,但是Vector是一个较老的集合,具有很多缺点,不建议使用。 4)ArrayList和Collection的关系

小码农吗 465

ArrayList底层源码分析--扩容机制、构造方法、add方法

ArrayList底层源码分析--扩容机制、构造方法、add方法

温涛的博客 218

Java容器:ArrayList源码分析(2 add方法)

在list后面插入一个元素。* 指定插入位置(下标)插入元素。

20年码农 376
上一篇: 深入理解MVC
下一篇: JSP运行原理分析
zcczero
博客等级 码龄9年 38粉丝 24原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值