ArrayList添加一个元素的过程(中部插入以及尾部添加)
只知道在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
}
进入ensureExplicitCapacity
,if (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
开始以后的元素,复制到elementData
的index+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开头的),截取了三个数。截取到的新数组,向目标数组elementData
以index+1
:4 ,为起始位置进行复制添加。刚刚就中间就空出了一个索引为index:3 的位置,然后在执行下面两句话,将指定元素赋值到索引位置,然后size+1 ,就完成了元素从中间插入的过程。
elementData[index] = element;
size++;
执行结果:
ArrayList添加一个元素的过程(中部插入以及尾部添加)相关推荐
- R语言dplyr包为dataframe添加数据列实战( Add Columns):基于mutate()函数添加一个或者多个数据列(尾部添加、头部添加、条件生成、某个具体数据列的前后)
R语言dplyr包为dataframe添加数据列实战( Add Columns):基于mutate()函数添加一个或者多个数据列(尾部添加.头部添加.条件生成.某个具体数据列的前后) 目录
- 【Groovy】集合遍历 ( 操作符重载 | 集合中的 “ << “ 操作符重载 | 使用集合中的 “ << “ 操作符添加一个元素 | 使用集合中的 “ << “ 操作符添加一个集合 )
文章目录 一.集合中的 " << " 操作符重载 1.使用集合中的 " << " 操作符添加一个元素 2.使用集合中的 " & ...
- java数组末尾添加元素_java数组添加元素,java数组如何添加一个元素
java数组如何添加元素 向数组里添加一个元素怎么添加,这儿总结有三种方法: 1.一般数组是不能添加元素的,因为他们在初始化时就已定好长度了,不能改变长度. 但有个可以改变大小的数组为ArrayLis ...
- 【Groovy】map 集合 ( map 集合操作符重载 | 使用 << 操作符添加一个元素 | 代码示例 )
文章目录 一.使用 " << " 操作符添加一个元素 二.代码示例 一.使用 " << " 操作符添加一个元素 对 map 集合 使用 ...
- kotlin List删除一个元素,添加一个元素
kotlin List移除一个元素,添加一个元素时没有 remove和add函数只有 -= 和 += 在kotlin ArrayList中才有remove和add函数 没有 -= 和 += kotli ...
- JavaScript 添加一个元素标签
JavaScript 添加一个元素标签 文章目录 JavaScript 添加一个元素标签 代码 效果 代码 <!DOCTYPE html> <html><head> ...
- 动态数组,数组初始化,数组内存释放,向数组中添加一个元素,向数组中添加多个元素,数组打印,顺序查找,二分查找,查找数组并返回地址,冒泡排序,改变数组中某个元素的值,删除一个数值,删除所有,查找含有
1定义接口: Num.h #ifndef_NUM_H_ #define_NUM_H_ #include<stdio.h> #include<stdlib.h> /**** ...
- 原生js清空上一个元素内容_原生JS实现动态添加新元素、删除元素方法
1. 添加新元素 动态添加新元素 Coffee Tea Coffee Tea var child = document.getElementsByClassName("child" ...
- html的美图片加上2d动画,网站上面添加一个动漫小女孩的html代码 WordPress添加live2d看板娘...
在你博客程序头部文件(header.php)引入界面样式,在 head 标签内插入如下代码 在你博客程序页脚文件(footer.php)引入脚本,在最后一个 标签前插入如下代码: var messag ...
最新文章
- Visual studio 2005如何实现源码管理
- 2020年全球数据中心基础设施收入1650亿美元
- 一次解决libgcc_s.so.1 must be installed for pthread_cancel to work的经历
- shell 判断字符串相等_编程小短文:Bash子字符串还在用==?试试=~性能瞬间飙升100倍...
- mybatis思维导图,让mybatis不再难懂(二)
- javaserver_集成Spring和JavaServer Faces:改进的模板
- skiplist原理与实现
- 【月报】Java知音的五月汇总
- 非计算机专业《Python程序设计基础》教学参考大纲
- [Project Euler] Problem 26
- LightOJ 1258 Making Huge Palindromes(KMP)
- Spring boot 自定义拦截器 获取 自定义注解 信息
- 大一html网页制作期末网页设计——环境保护-小黄车(5页) HTML+CSS+JavaScript 学生DW网页设计作业成品 环保生态 绿色环境
- mac DOSBox快捷键
- PHPstorm设置字体大小
- 利用Map,完成下面的功能: 从命令行读入一个字符串,表示一个年份,输出该年的世界杯冠军是哪支球队。如果该年没有举办世界杯,则输出:没有举办世界杯。
- 【Unity】四叉树/八叉树管理和动态加载场景物件
- 关于地理坐标的精度设置,做测绘的工程师应该懂的基本常识(南方数码CASS11.0.0.6还增加批量转换的方式)
- 个人总结的新手看房买房注意事项,有遗漏的地方请各位同学帮忙补充
- 使用Office Tool Plus安装office、visio、project等--很实用