只知道在ArrayList 添加一个元素在尾部添加元素,如果容量不够就会扩容1.5倍,也没有通过源码去研究过这个过程。今天我们就来研究研究:
中间插入,和末尾插入 这两种方式 来进行研究。

尾部添加

首先我们实现add方法

@Test
public void testEndAdd(){ArrayList arr = new ArrayList();arr.add(1);arr.add(1);
}

点进add方法我们可以看到add里面有一个ensureCapacityInternal方法,并且传了一个size(list里面数据的长度)+1的参数到这个方法里面

elementData 是Arraylist中存放元素的真正数组,size是当前数组中存了几个元素,而不是数组的长度!!!

public boolean add(E e) {ensureCapacityInternal(size + 1);  // Increments modCount!!elementData[size++] = e;return true;
}

点进ensureCapacityInternal方法,我们可以看到我们传进来的size+1是minCapacity这个参数,这个参数就是加入一个元素后元素的个数

private void ensureCapacityInternal(int minCapacity) {//判断数组是不是空,我们第一次的添加是空,minCapacity就变成了DEFAULT_CAPACITY--> 10if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);}//确定是否需要扩容,下一步进入ensureExplicitCapacity(minCapacity);
}

点击进入ensureExplicitCapacity
我们首先看到是一个modCount++ 并且在ArrayList,LinkedList,HashMap等等的内部实现增,删,改中我们总能看到modCount的身影,modCount字面意思就是修改次数所有使用modCount属性的全是线程不安全的,而且只有在本数据结构对应迭代器中才使用。 这里我不深究,大家可以参考这篇文章

    private void ensureExplicitCapacity(int minCapacity) {modCount++;// overflow-conscious code//判断minCapacity(元素的个数)是否大于  elementData.length(数组的长度),这里因为是初始化所以是10,其他情况不一样//进入grow方法if (minCapacity - elementData.length > 0)grow(minCapacity);}
    private void grow(int minCapacity) {// overflow-conscious codeint oldCapacity = elementData.length;//由于是第一次添加oldCapacity = 0int newCapacity = oldCapacity + (oldCapacity >> 1);//这里是扩容到的1.5倍,所以newCapacity = 0//情况一://如果扩容后的长度还是小于当前需要存入的个数minCapacity ,为扩容为minCapacity 的大小if (newCapacity - minCapacity < 0)//判断是否大于0,这里是 0-10 < 0newCapacity = minCapacity;//newCapacity  = 10//情况二://判断传入的参数minCapacity是否大于MAX_ARRAY_SIZE,如果minCapacity大于MAX_ARRAY_SIZE,返回Integer.MAX_VALUE,否者返回MAX_ARRAY_SIZEif (newCapacity - MAX_ARRAY_SIZE > 0)newCapacity = hugeCapacity(minCapacity);//这个Arrays.copyOf()我们很熟悉,程序走到这里就代表老老实实的扩容了。将原来的数据也复制在这个数组中。elementData = Arrays.copyOf(elementData, newCapacity);}

这里我们也不深究这个还与jvm储存头部字有关系,而且我们一般也用不到这么大的空间,有兴趣看这篇文章

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

到下一步,这个代码看起就有点打脑壳,意思就是说先判断这个数组的是不是Object类型的,如果是直接创建一个的Object的数组Object[newLength] 数组,这个newLength就是我们需要扩容后的大小,如果不是Object的就直接new一个目标类型,长度也是newLength

    public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {@SuppressWarnings("unchecked")T[] copy = ((Object)newType == (Object)Object[].class)? (T[]) new Object[newLength]: (T[]) Array.newInstance(newType.getComponentType(), newLength);//最终目的就是保证传入给System.arraycopy() 方法中参数"copy" 的值是一个我们扩容后的新数组System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));return copy;}
public boolean add(E e) {ensureCapacityInternal(size + 1);  // Increments modCount!!//把添加的元素放入数组中已存入元素个数的+1位置!elementData[size++] = e;return true;
}

自此完成了尾部的添加

中部插入

插入代码

@Test
public void testMidAdd(){ArrayList arr = new ArrayList();arr.add(1);arr.add(1);arr.add(1);arr.add(1);arr.add(1);arr.add(3,2);
}

进入add方法

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++;
}

进入ensureCapacityInternal

private void ensureCapacityInternal(int minCapacity) {//判断数组是否为空,这里不为空if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);}//直接跳到这一步ensureExplicitCapacity(minCapacity);//minCapacity = 6
}

进入ensureExplicitCapacityif (minCapacity - elementData.length > 0)这个是判断minCapacity (元素个数)与elementData.length(数组长度)的大小关系,如果元素个数大于数组长度就会进入grow方法进行扩容,这里面就与上面尾部添加的流程一样了。这里minCapacity (元素个数)- elementData.length(数组长度)= 6 - 10 < 0,所以就不进入grow方法。

private void ensureExplicitCapacity(int minCapacity) {//修改次数+1modCount++;// overflow-conscious codeif (minCapacity - elementData.length > 0)grow(minCapacity);
}

然后就会回到add方法 进行System.arraycopy

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++;
}

核心语句

如果不需要扩容的话(需要扩容的话就先扩容),系统会将index下标以后的元素,统一向后移动一位,也就是把elementData中从index开始以后的元素,复制到elementDataindex+1的位置,复制个数呢,就是index以后的所有元素,也就是size-index个,这里源数组和目标数组一样。

System.arraycopy(elementData, index, elementData, index + 1,size - index);

我们可以点进去看一下每个参数的含义:

public static native void arraycopy(Object src, int  srcPos, Object dest, int destPos, int length);

五个参数分别为
src:源数组
srcPos:从源数组的下标开始复制
dest:目标数组
destPos:目标数组的下标开始
length:复制多少个元素

我们插入的是这段代码,我们对其进行分析

@Test
public void testMidAdd(){ArrayList arr = new ArrayList();arr.add(1);arr.add(1);arr.add(1);arr.add(1);arr.add(1);arr.add(3,2);
}

这里就是 从源数组elementData以index作为起始位置,截取长度为size - index:5 - 3(size是以0开头的),截取了三个数。截取到的新数组,向目标数组elementDataindex+1:4 ,为起始位置进行复制添加。刚刚就中间就空出了一个索引为index:3 的位置,然后在执行下面两句话,将指定元素赋值到索引位置,然后size+1 ,就完成了元素从中间插入的过程。

elementData[index] = element;
size++;

执行结果:

ArrayList添加一个元素的过程(中部插入以及尾部添加)相关推荐

  1. R语言dplyr包为dataframe添加数据列实战( Add Columns):基于mutate()函数添加一个或者多个数据列(尾部添加、头部添加、条件生成、某个具体数据列的前后)

    R语言dplyr包为dataframe添加数据列实战( Add Columns):基于mutate()函数添加一个或者多个数据列(尾部添加.头部添加.条件生成.某个具体数据列的前后) 目录

  2. 【Groovy】集合遍历 ( 操作符重载 | 集合中的 “ << “ 操作符重载 | 使用集合中的 “ << “ 操作符添加一个元素 | 使用集合中的 “ << “ 操作符添加一个集合 )

    文章目录 一.集合中的 " << " 操作符重载 1.使用集合中的 " << " 操作符添加一个元素 2.使用集合中的 " & ...

  3. java数组末尾添加元素_java数组添加元素,java数组如何添加一个元素

    java数组如何添加元素 向数组里添加一个元素怎么添加,这儿总结有三种方法: 1.一般数组是不能添加元素的,因为他们在初始化时就已定好长度了,不能改变长度. 但有个可以改变大小的数组为ArrayLis ...

  4. 【Groovy】map 集合 ( map 集合操作符重载 | 使用 << 操作符添加一个元素 | 代码示例 )

    文章目录 一.使用 " << " 操作符添加一个元素 二.代码示例 一.使用 " << " 操作符添加一个元素 对 map 集合 使用 ...

  5. kotlin List删除一个元素,添加一个元素

    kotlin List移除一个元素,添加一个元素时没有 remove和add函数只有 -= 和 += 在kotlin ArrayList中才有remove和add函数 没有 -= 和 += kotli ...

  6. JavaScript 添加一个元素标签

    JavaScript 添加一个元素标签 文章目录 JavaScript 添加一个元素标签 代码 效果 代码 <!DOCTYPE html> <html><head> ...

  7. 动态数组,数组初始化,数组内存释放,向数组中添加一个元素,向数组中添加多个元素,数组打印,顺序查找,二分查找,查找数组并返回地址,冒泡排序,改变数组中某个元素的值,删除一个数值,删除所有,查找含有

     1定义接口: Num.h #ifndef_NUM_H_ #define_NUM_H_ #include<stdio.h> #include<stdlib.h> /**** ...

  8. 原生js清空上一个元素内容_原生JS实现动态添加新元素、删除元素方法

    1. 添加新元素 动态添加新元素 Coffee Tea Coffee Tea var child = document.getElementsByClassName("child" ...

  9. html的美图片加上2d动画,网站上面添加一个动漫小女孩的html代码 WordPress添加live2d看板娘...

    在你博客程序头部文件(header.php)引入界面样式,在 head 标签内插入如下代码 在你博客程序页脚文件(footer.php)引入脚本,在最后一个 标签前插入如下代码: var messag ...

最新文章

  1. Visual studio 2005如何实现源码管理
  2. 2020年全球数据中心基础设施收入1650亿美元
  3. 一次解决libgcc_s.so.1 must be installed for pthread_cancel to work的经历
  4. shell 判断字符串相等_编程小短文:Bash子字符串还在用==?试试=~性能瞬间飙升100倍...
  5. mybatis思维导图,让mybatis不再难懂(二)
  6. javaserver_集成Spring和JavaServer Faces:改进的模板
  7. skiplist原理与实现
  8. 【月报】Java知音的五月汇总
  9. 非计算机专业《Python程序设计基础》教学参考大纲
  10. [Project Euler] Problem 26
  11. LightOJ 1258 Making Huge Palindromes(KMP)
  12. Spring boot 自定义拦截器 获取 自定义注解 信息
  13. 大一html网页制作期末网页设计——环境保护-小黄车(5页) HTML+CSS+JavaScript 学生DW网页设计作业成品 环保生态 绿色环境
  14. mac DOSBox快捷键
  15. PHPstorm设置字体大小
  16. 利用Map,完成下面的功能: 从命令行读入一个字符串,表示一个年份,输出该年的世界杯冠军是哪支球队。如果该年没有举办世界杯,则输出:没有举办世界杯。
  17. 【Unity】四叉树/八叉树管理和动态加载场景物件
  18. 关于地理坐标的精度设置,做测绘的工程师应该懂的基本常识(南方数码CASS11.0.0.6还增加批量转换的方式)
  19. 个人总结的新手看房买房注意事项,有遗漏的地方请各位同学帮忙补充
  20. 使用Office Tool Plus安装office、visio、project等--很实用

热门文章

  1. 最大子矩阵和问题java_最大子矩阵
  2. 计算机应用基础教师授课视频,利用微课促进《计算机应用基础》教学的有效途径...
  3. MMKV使用及简单封装-kotlin
  4. 什么是对象?对象的属性和存储又是什么?
  5. Spark数据挖掘实例1:基于 Audioscrobbler 数据集音乐推荐
  6. strchr()函数用法及其详解
  7. 【关于memset和0x3f3f3f3f】
  8. for循环去掉最后一个逗号(三种方法)
  9. 一个39岁程序员的应聘被拒 | 文末送书
  10. Js如何删除所有子元素以及当前元素