使用ArrayList时代码内部发生了什么(jdk1.7)?
前言
ArrayList(这里的ArrayList是基于jdk1.7)是在项目中经常使用的集合类,例如我们从数据库中查询出一组数据。这篇文章不去剖析它的继承和实现,只是让我们知道实例化及增删改查时它的内部代码是怎么实现的。
public class TestList {
@Test
public void testArrayList(){
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 12; i++) {
list.add(i);
}
}
}
实例化
List<Integer> list = new ArrayList<>();
我们先来看看上面这段实例化ArrayList时,内部发生了什么。
这调用的是ArrayList的无参构造函数,如下
public ArrayList() {
super();
this.elementData = EMPTY_ELEMENTDATA;
}
步骤:
- 调用父类的构造函数(AbstractList的构造函数)
- 赋值elementData(一个空数组{})
这里把ArrayList内部的属性说明下,一共有DEFAULT_CAPACITY、EMPTY_ELEMENTDATA、elementData、size。其中DEFAULT_CAPACITY和EMPTY_ELEMENTDATA是被定义为static和final的。而elementData是我们添加元素时存储的数组,size就是这个数组的大小,DEFAULT_CAPACITY就是数组默认的大小,EMPTY_ELEMENTDATA是一个空数组。
/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10; /**
* Shared empty array instance used for empty instances.
*/
private static final Object[] EMPTY_ELEMENTDATA = {}; /**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == EMPTY_ELEMENTDATA will be expanded to
* DEFAULT_CAPACITY when the first element is added.
*/
private transient Object[] elementData; /**
* The size of the ArrayList (the number of elements it contains).
*
* @serial
*/
private int size;
实例化部分就到这里了,其他有参的这里就不做介绍,需要的可以自己去看看,接下来看看添加元素
添加元素
for (int i = 0; i < 12; i++) {
list.add(i);
}
这里循环添加了12个元素,是为了查看ArrayList的第一次扩容,接下来看看ArrayList里面是怎么实现的
add方法
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
add方法中的步骤如下
- 调用ensureCapacityInternal,传入参数(size+1)
- elementData数组对象赋值
- 返回true
ensureCapacityInternal方法
private void ensureCapacityInternal(int minCapacity) {
if (elementData == EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
这个方法中传入的参数名称为minCapacity,翻译为中文就是最小容量。
这个方法首先判断当前数组对象elementData是不是等于空对象EMPTY_ELEMENTDATA。大家可以看下实例化时elementData对象的赋值(如下)。
public ArrayList() {
super();
this.elementData = EMPTY_ELEMENTDATA;
}
如果是,则最小容量minCapacity重新赋值为DEFAULT_CAPACITY和minCapacity中最大的一个数。
然后调用ensureExplicitCapacity方法。断点内容如下
minCapacity被赋值为DEFAULT_CAPACITY
接下来是ensureExplicitCapacity方法
ensureExplicitCapacity方法
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
modCount是ArrayList的父类AbstractList的一个属性,记录被修改的次数,也是通过这个触发fail-fast机制的,这里不过多说明。
下面就是比较传入的最小容量minCapacity减去elementData数组的长度是否大于0(也就是最小容量minCapacity是否大于elementData数组的长度),是的话调用grow方法。
grow方法
这个方法就是ArrayList的扩容方法,接下来看看方法内部代码
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
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);
}
由于是第一次调用add方法,所以minCapacity值为ArrayList的默认容量10,如下
- 接下来定义一个旧容量oldCapacity,值为elementData数组长度
- 然后定义一个新容量newCapacity,值为旧容量oldCapacity加上(旧容量oldCapacity右移1位,也就是除以2,不四舍五入)
- 接着比较新容量newCapacity和传入的最小容量minCapacity,谁大就赋值给新容量newCapacity
- 如果新容量newCapacity比定义的数组最大容量MAX_ARRAY_SIZE还大的话,就调用hugeCapacity方法,看是否抛出异常还是赋最大值
- 调用Arrays.copyOf进行数组扩容
获取元素
获取元素有三种方式
- 迭代器Iterator遍历
- 通过索引
- foreach循环获取
这里就介绍下通过迭代器Iterator遍历吧。
public Iterator<E> iterator() {
return new Itr();
}
Itr是ArrayList一个实现Iterator的内部类,通过这个对象来获取ArrayList中存储的元素。代码如下
/**
* An optimized version of AbstractList.Itr
*/
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount; public boolean hasNext() {
return cursor != size;
} @SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
} public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification(); try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
} final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
总结
当我们实例化ArrayList时(调用无参构造函数),这个对象里面存储元素的数组还只是一个空数组。
在第一次调用add方法时(也就是第一次添加元素时),对存储元素的数组elementData进行第一次扩容,扩容的数组长度为ArrayList内部定义的默认容量10,。
当插入第11个数组元素时,进行第二次扩容,扩容的长度为原先的容量加上(原先容量/2),即原先容量的1.5倍。扩容的长度规则是10>>15>>22>>33...
使用ArrayList时代码内部发生了什么(jdk1.7)?的更多相关文章
- 当你创建了一个 Deployment 时,Kubernetes 内部发生了什么?
我们通常使用 kubectl 来管理我们的 Kubernetes 集群. 当我们需要一个 Nginx 服务时,可以使用以下命令来创建: kubectl create deployment nginx ...
- ArrayList底层代码解析笔记
通过底层代码可以学习到很多东西: public class ArrayList<E> extends AbstractList<E> implements List<E& ...
- java虚拟机jvm启动后java代码层面发生了什么?
java虚拟机jvm启动后java代码层面发生了什么? 0000 我想验证的事情 java代码在被编译后可以被jdk提供的java命令进行加载和运行, 在我们的程序被运行起来的时候,都发生了什么事情, ...
- 【ITOO 2】使用ArrayList时的注意事项:去除多余的null值
问题描述:在课表导入的时候,将数据从excel表里读出,然后将list批量插入到对应的课程表的数据表单中去,出现结果:当我们导入3条数据时,list.size()为3,但是实际上,list里面存在10 ...
- 换个视角来看git命令与代码库发生网络交互报错事件
git的一系列命令中像 clone.pull.push等与代码库发生网络交互时,可能报下面的错误信息 fatal: remote error: CAPTCHA required Your Stash ...
- 实习日记)select option 选择不同的option时, 页面发生不同的变化
怎么在下拉框的选择不同的option时, 页面发生响应的变化 因为option是没有点击事件什么的, 只有select才有, 所以不能通过option的点击事件来完成, 所以开始的尝试都失败了(之前 ...
- 当C#中带有return的TryCatch代码遇到Finally时代码执行顺序
编写的代码最怕出现的情况是运行中有错误出现,但是无法定位错误代码位置.综合<C#4.0图解教程>,总结如下: TryCatchFinally用到的最多的是TryCatch,Catch可以把 ...
- 如何将oc代码转换成运行时代码
// 运行时 其实就是oc的底层 平时写的代码 最终都是转成底层的运行时代码以下面程序为例子: 如果我们想要看我们的main.m文件底层转换成了怎样的运行时代码 ,我们可以这样做. 1.打开终端 ...
- 《Secrets of the JavaScript Ninja》:JavaScript 之运行时代码
最近,在阅读 jQuery 之父 John Resig 力作:Secrets of the JavaScript Ninja(JavaScript忍者秘籍).关于第九章提及的 JavaScript 之 ...
随机推荐
- Emacs配置(考场必备)(Emacs)
最近有几次离开自己一直坐着的座位,去别的机房考试了. 于是猛然想起来要记一记Emacs的简洁配置了. 算是把NOIP残存的记忆再拾一点起来...... 附上一些解释 (global-set-key [ ...
- [HNOI2011]XOR和路径 && [HNOI2013]游走
[HNOI2011]XOR和路径 题目大意 具体题目:戳我 题目: 给定一个n个点,m条边的有重边.有自环的无向图,其中每个边都有一个边权. 现在随机选择一条1到n的路径,路径权值为这条路径上所有边权 ...
- [BZOJ1861][ZJOI2006]书架
BZOJ Luogu Description 小T有一个很大的书柜.这个书柜的构造有些独特,即书柜里的书是从上至下堆放成一列.她用1到n的正整数给每本书都编了号. 小T在看书的时候,每次取出一本书,看 ...
- MyBatis笔试题
1请写出Mybatis核心配置文件MyBatis-config.xml的内容? <?xml version="1.0" encoding="UTF-8"? ...
- vue.js 配置移动端的url Scheme和iOS端配置url Scheme
假如urlScheme:baibai:// 一.vue.js端的配置: 1.通过html标签跳转: <p href="baibai://"></p> 2.通 ...
- 在线教育平台搭建 预览和models
一.前言 1.1.项目介绍 在线演示地址:mxonline.mtianyan.cn 开发环境: python:3.6.4 Django:2.0.2 后台管理:xadmin 系统概括: 系统具有完整的用 ...
- 使用python+Selenium对空调控制器进行循环发送控制命令
今天一同事说想对空调控制器进行循环发送命令操作.经过了对控制流程的梳理,发现每次选择内机后进入控制页面设定温度都是在26度,想了想,如果要进行循环就得将设定温度重置为17度,然后每循环一次温度增加1度 ...
- java单点登录原理与简单实现
一.单系统登录机制 1.http无状态协议 web应用采用browser/server架构,http作为通信协议.http是无状态协议,浏览器的每一次请求,服务器会独立处理,不与之前或之后的请求产生关 ...
- Cesium 获取鼠标当前位置的模型高度,地形高度,OSGB高度,及其经纬度。
var handler = new Cesium.ScreenSpaceEventHandler(viewer.scene._imageryLayerCollection);var ray,posit ...
- .Net小白的大学四年,内含面经
大家好 我是码农阿宇,和博客园的广大兄弟一样,我们都喜欢.Net,但是你们是985/211,而我江西一所普通得不能再普通的二本大学---九江学院,大四毕业在即,英语四级未过(为什么强调这一点?见文末- ...