jdk1.8-ArrayList源码分析

public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
继承AbstracList抽象父类
private static final long serialVersionUID = 8683452581122892189L;
/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* Shared empty array instance used for empty instances.
*
*/
private static final Object[] 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 = {};
/**
* 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 == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
*/
transient Object[] elementData; // non-private to simplify nested class access
/**
* The size of the ArrayList (the number of elements it contains).
*
* @serial
*/
private int size;
/**
* Constructs an empty list with the specified initial capacity.
*
* @param initialCapacity the initial capacity of the list
* @throws IllegalArgumentException if the specified initial capacity
* is negative
*/
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
//如果传进来的长度大于0,则直接初始化Object[]传进来的长度数组
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
//如果长度为0则让数组等于Object[] EMPTY_ELEMENTDATA = {};空数组
this.elementData = EMPTY_ELEMENTDATA;
} else {
//抛错
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
/**
* 不传参,初始化一个空数组,与上面空数组不为同一个
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* 当传递的参数为集合类型时,会把集合类型转化为数组类型,并赋值给elementData
*/
public ArrayList(Collection<? extends E> c) {
//先转为数组
elementData = c.toArray();
//参数长度不为0
if ((size = elementData.length) != 0) {
//如果没有成功转为Object型数组,Object[].class得到的是class [Ljava.lang.Object;
if (elementData.getClass() != Object[].class)
//那么就进行拷贝
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
/**
* 将指定的元素添加到此列表的尾部。
*/
public boolean add(E e) {
//确保数组有合适的大小
ensureCapacityInternal(size + 1); // Increments modCount!!
//放入对应的数组下表
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
//如果elementData数组等于默认空元素数组则进入下面的逻辑(ps:我们假设是不传参初始化的ArrayList,所以会进入下面括号内的逻辑)
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//DEFAULT_CAPACITY=10,比较两者数的大小,较大值
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
//将参数继续传入该函数
ensureExplicitCapacity(minCapacity);
}
分析:这里minCapacity传入的时候为1,经过比较取大值后,minCapacity为10。
private void ensureExplicitCapacity(int minCapacity) {
//记录修改次数加1
modCount++;
//当前传入minCapacity长度大于当前数组长度
if (minCapacity - elementData.length > 0)
//将参数传入该函数
grow(minCapacity);
}
分析:modCount++,然后执行grow()方法
/**
* 真正执行数组扩容的方法,先判断扩容的长度,最后执行Arrays.copyOf扩容
*/
private void grow(int minCapacity) {
//当前旧的数组长度
int oldCapacity = elementData.length;
//新数组长度等于 = 旧数组长度 + 旧数组长度除以2
int newCapacity = oldCapacity + (oldCapacity >> 1);
//小于传进来的参数则等于传进来的参数
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//大于int的最大长度(2^31 - 1) 再减去8长度,传入minCapacity参数,执行hugeCapacity()方法,指定新容量
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
//执行Arrays.copyOf将旧数组扩容成新数组长度为newCapacity
elementData = Arrays.copyOf(elementData, newCapacity);
}
分析:在我们假设的前提下,minCapacity当前等于10,当前oldCapacity为0。
/**
* 将指定的元素添加到此列表的尾部。
*/
public boolean add(E e) {
//确保数组有合适的大小
ensureCapacityInternal(size + 1); // Increments modCount!!
//
elementData[size++] = e;
return true;
}
分析:这里现在ensureCapacityInternal()传入的参数为2
private void ensureCapacityInternal(int minCapacity) {
//如果elementData数组等于默认空元素数组则进入下面的逻辑(ps:我们前面假设是不传参初始化的ArrayList,所以会进入下面括号内的逻辑)
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//DEFAULT_CAPACITY=10,比较两者数的大小,较大值
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
//将参数等于10继续传入该函数
ensureExplicitCapacity(minCapacity);
}
分析:第二次添加,那么minCapacity就等于2。也就是minCapacity可以理解为当前元素个数。
private void ensureExplicitCapacity(int minCapacity) {
//记录修改次数加1
modCount++;
//当前传入minCapacity长度大于当前数组长度
if (minCapacity - elementData.length > 0)
//将参数传入该函数
grow(minCapacity);
}
分析:到这里就是记录修改次数加1了,2小于当前数组长度10,不满足,不往下执行。
private void ensureExplicitCapacity(int minCapacity) {
//记录修改次数加1
modCount++;
//当前传入minCapacity长度大于当前数组长度
if (minCapacity - elementData.length > 0)
//将参数传入该函数
grow(minCapacity);
}
分析:我们从这里很容易就看出,第二次准备扩容时,elementData.length当前的数组长度是10。当前的元素个数minCapacity为11个时,放不下,也就是数组个数已经满了,开始扩容。
/**
* 真正执行数组扩容的方法,先判断扩容的长度,最后执行Arrays.copyOf扩容
*/
private void grow(int minCapacity) {
//当前旧的数组长度
int oldCapacity = elementData.length;
//新数组长度等于 = 旧数组长度 + 旧数组长度除以2 (也就是1.5倍)
int newCapacity = oldCapacity + (oldCapacity >> 1);
//小于传进来的参数则等于传进来的参数
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//大于int的最大长度(2^31 - 1)再减去8长度,传入minCapacity参数,执行hugeCapacity()方法
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
//执行Arrays.copyOf将旧数组扩容成新数组长度为newCapacity
elementData = Arrays.copyOf(elementData, newCapacity);
}
分析:此时,newCapactiy值等于旧数组的1.5倍,然后进行复制扩容。也就是说,第二次包括以后数组满了再扩容,在满足没有超过MAX_ARRAY_SIZE的前提下每次数组扩容都是扩容为原来数组长度的1.5倍长。
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8
//当前传入minCapacity长度大于当前数组长度
if (minCapacity - elementData.length > 0)
//将参数传入该函数
grow(minCapacity);
/**
* 通过移动数组后面所有的元素覆盖当前索引元素,从而达到删除当前元素的效果
* 数组最后一个索引值置为null,说明ArrayList可以查找null值
*/
public E remove(int index) {
//检查删除的索引是否超出数组的索引
rangeCheck(index);
//修改记录
modCount++;
//根据索引获取数组值
E oldValue = elementData(index);
//需要移动的元素个数
int numMoved = size - index - 1;
if (numMoved > 0)
//删除元素索后面的所有元素都要往前移动一个索引
//也表明了数组的删除是通过后面的元素往前移动进行覆盖而达到删除的目的
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
//数组最后一个索引值置为null,并且索引减1
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
/**
* 因为底层是数组,所以set时,可以直接根据索引进行覆盖旧的值
*/
public E set(int index, E element) {
//检查索引是否越界
rangeCheck(index);
E oldValue = elementData(index);
//直接根据索引覆盖值
elementData[index] = element;
return oldValue;
}
/**
* 从首部开始查找,返回第一个值相同的索引,遍历完没找到返回-1
*/
public int indexOf(Object o) {
//传进来的值为null,从首部开始遍历数组,找到第一个为null的值,返回当前索引
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
//不为空,从首部遍历,返回第一个值相同的下标索引
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
//没找到返回-1
return -1;
}

jdk1.8-ArrayList源码分析的更多相关文章
- ArrayList源码分析--jdk1.8
ArrayList概述 1. ArrayList是可以动态扩容和动态删除冗余容量的索引序列,基于数组实现的集合. 2. ArrayList支持随机访问.克隆.序列化,元素有序且可以重复. 3. ...
- Java基础 ArrayList源码分析 JDK1.8
一.概述 本篇文章记录通过阅读JDK1.8 ArrayList源码,结合自身理解分析其实现原理. ArrayList容器类的使用频率十分频繁,它具有以下特性: 其本质是一个数组,因此它是有序集合 通过 ...
- java集合系列之ArrayList源码分析
java集合系列之ArrayList源码分析(基于jdk1.8) ArrayList简介 ArrayList时List接口的一个非常重要的实现子类,它的底层是通过动态数组实现的,因此它具备查询速度快, ...
- Java集合干货——ArrayList源码分析
ArrayList源码分析 前言 在之前的文章中我们提到过ArrayList,ArrayList可以说是每一个学java的人使用最多最熟练的集合了,但是知其然不知其所以然.关于ArrayList的具体 ...
- ArrayList 源码分析
ArrayList 源码分析 1. 结构 首先我们需要对 ArrayList 有一个大致的了解就从结构来看看吧. 1. 继承 该类继承自 AbstractList 这个比较好说 2. 实现 这 ...
- ArrayList源码分析超详细
ArrayList源码分析超详解 想要分析下源码是件好事,但是如何去进行分析呢?以我的例子来说,我进行源码分析的过程如下几步: 找到类:利用 IDEA 找到所需要分析的类(ztrl+N查找ArraLi ...
- Java - ArrayList源码分析
java提高篇(二一)-----ArrayList 一.ArrayList概述 ArrayList是实现List接口的动态数组,所谓动态就是它的大小是可变的.实现了所有可选列表操作,并允许包括 nul ...
- ArrayList源码分析超详细(转载)
ArrayList源码分析超详细 ArrayList源码分析超详解 想要分析下源码是件好事,但是如何去进行分析呢?以我的例子来说,我进行源码分析的过程如下几步: 找到类:利用 IDEA 找到所需要 ...
- Java ArrayList源码分析(有助于理解数据结构)
arraylist源码分析 1.数组介绍 数组是数据结构中很基本的结构,很多编程语言都内置数组,类似于数据结构中的线性表 在java中当创建数组时会在内存中划分出一块连续的内存,然后当有数据进入的时候 ...
- Java入门系列之集合ArrayList源码分析(七)
前言 上一节我们通过排队类实现了类似ArrayList基本功能,当然还有很多欠缺考虑,只是为了我们学习集合而准备来着,本节我们来看看ArrayList源码中对于常用操作方法是如何进行的,请往下看. A ...
随机推荐
- go变量和数据类型
go语言的基本数据类型 布尔类型:bool 整型:int8.byte.int16.int.uint.uintptr等 浮点类型:float32.float64 复数类型:comple ...
- npm更换成淘宝镜像源以及cnpm
1.需求由来 由于node安装插件是从国外服务器下载,受网络影响大,速度慢且可能出现异常.所以如果npm的服务器在中国就好了,所以我们乐于分享的淘宝团队(阿里巴巴旗下业务阿里云)干了这事.来自官网:“ ...
- CF875F Royal Questions[最大生成基环树森林]
这题这场比赛一堆人秒切..果然还是我太菜了吗 题意:二分图,右边$m$个点每个点$i$向左边有且仅有两条连边,边权都是$a_i$.求最大匹配. 一个朴素思想,二分图匹配,用贪心带匈牙利搞一搞,但是复杂 ...
- Myeclipse中左边的项目目录没了
切换一下Perspective, java, resource都有项目窗口 具体的 Window->Open Perspective 如果项目窗口被关了的话 windows->show v ...
- Linux配置python环境2,安装python
安装锁需要的依赖包 sudo apt-get -y install gcc-5 g++-5 libc6-dev make build-essential libssl-dev zlib1g-dev l ...
- 如何制作纯净的U盘启动盘
1.去下载**WinPE工具箱**U盘启动盘制作工具 下载地址:http://www.wepe.com.cn/download.html
- ES大批量写入提高性能的策略
1.用bulk批量写入 你如果要往es里面灌入数据的话,那么根据你的业务场景来,如果你的业务场景可以支持让你将一批数据聚合起来,一次性写入es,那么就尽量采用bulk的方式,每次批量写个几百条这样子. ...
- hdu 5723 Abandoned country 最小生成树+子节点统计
Abandoned country Time Limit: 8000/4000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others ...
- sh_19_字符串拆分和拼接
sh_19_字符串拆分和拼接 # 假设:以下内容是从网络上抓取的 # 要求: # 1. 将字符串中的空白字符全部去掉 # 2. 再使用 " " 作为分隔符,拼接成一个整齐的字符串 ...
- 【IOI2018】机械娃娃
看到的时候感到很不可做,因为所有的开关都要状态归零.因此可以得到两分的好成绩. --然后 yhx-12243 说:这不是线段树优化建图吗? 于是我获得了启发,会做了-- 还不是和上次一样,通过提示做出 ...