使用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 之 ...
随机推荐
- 【CJOJ1167】【洛谷1894】[USACO4.2]完美的牛栏
题面 Description 农夫约翰上个星期刚刚建好了他的新牛棚,他使用了最新的挤奶技术.不幸的是,由于工程问题,每个牛栏都不一样.第一个星期,农夫约翰随便地让奶牛们进入牛栏,但是问题很快地显露出来 ...
- [Luogu4175][CTSC2008]网络管理Network
又是权限题qwq 一句话题意:带修改树上路径第k大 sol 数据结构?还是再见吧.学一手合格的整体二分,只有思维强大,才能见题拆题. 如果你做过整体二分的动态区间第k大就会发现这是一样的题. 无非是区 ...
- 10.socket网络编程
套接字工作流程 先从服务器端说起.服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接.在这时如果有个客户端初始化一个Sock ...
- pods 报错There may only be up to 1 unique SWIFT_VERSION per target
zhangpengdeMacBook-Pro:Jump zhangpeng$ pod install Analyzing dependencies [!] There may only be up t ...
- python web开发-flask访问请求数据request
Request对象在web应用的开发中是一个非常重要的对象,主要用来获取用户发来的请求数据. 常用属性参考:http://docs.jinkan.org/docs/flask/api.html#fla ...
- 《深入理解Bootstrap》读书笔记(二)
列表 1.普通列表 普通列表的使用没有什么变化,只是在原本的基础上对margin和行间距做了一些调整. 2.有序列表 有序列表的使用也是没有什么变化,只是在原本的基础上对margin和行间距做了一些调 ...
- 【django之博客系统开发】
一.项目简介 使用django开发一套博客系统,参考博客园. 需求如下: 项目结构: 二.全部代码 from django.db import models # Create your models ...
- 什么是IPFS?(一)
写在前面: 今天先写到这里, 关于IPFS的所有事情小编都想快点告诉大家, 但毕竟精力有限, 小编尽量抽出时间提供更多的关于IPFS的信息. ----------------------------- ...
- 来自后端的突袭? --浅尝最新开源的C# Web引擎 Blazor
在今年年初, 恰逢新春佳节临近的时候. 微软给全球的C#开发者们, 着实的送上了一分惊喜. 微软正式开源Blazor ,将.NET带回到浏览器. 这个小惊喜, 迅速的在dotnet开发者中间传开了. ...
- python函数式编程之yield表达式形式
先来看一个例子 def foo(): print("starting...") while True: res = yield print("res:",res ...