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 ...
随机推荐
- Summer training #8
A: B:按题意直接暴力找符合题意的数的个数 #include <bits/stdc++.h> #include <cstring> #include <iostream ...
- Cmd有关IP的部分命令
ping命令判断系统数据包在传送的时候至少会经过一个以上的路由器,当数据包经过一个路由器的时候,TTL就会自动减1,如果减到0了还是没有传送到目的主机,那么这个数据包就会自动丢失,这时路由器会发送一个 ...
- ffmpeg 在vs配置的库名称
avcodec.lib; avformat.lib; avutil.lib; avdevice.lib; avfilter.lib;postproc.lib;swresample.lib; swsca ...
- Java-五种线程池,四种拒绝策略,三种阻塞队列(转)
Java-五种线程池,四种拒绝策略,三种阻塞队列 三种阻塞队列: BlockingQueue<Runnable> workQueue = null; workQueue = n ...
- Servlet中关于路径的小结
URL(统一 资源定位符).URI(统一资源标识符)都是由资源路径和资源名称组成. 访问路径按照路径是否可以独立完成资源准确定位的判别标准,可以将访问路径分为绝对路径与相对路径. 关系:绝对路径 ...
- mysql explain 执行计划详解
1).id列数字越大越先执行,如果说数字一样大,那么就从上往下依次执行,id列为null的就表是这是一个结果集,不需要使用它来进行查询. 2).select_type列常见的有: A:simple ...
- VirtualBox:启动虚拟机后计算机死机
造冰箱的大熊猫@cnblogs 2018/2/21 故障描述:Ubuntu 16.04升级Linux内核后,在VirtualBox中启动虚拟机发现Ubuntu死机,只能通过长按电源开关硬关机的方式关闭 ...
- input 唤起键盘后遮住页面元素
var windheight = $(window).height(); /*未唤起键盘时当前窗口高度*/ $(window).resize(function(){ var docheight = $ ...
- 深度理解链式前向星——转载自ACdreamer
转载自ACdreamer [转载]深度理解链式前向星 我们首先来看一下什么是前向星. 前向星是一种特殊的边集数组,我们把边集数组中的每一条边按照起点从小到大排序,如果起点相同就按照终点从小到大排序 ...
- 记一次sparkOnyarn错误:java.lang.UnsatisfiedLinkError
错误大概这样: Caused by: java.util.concurrent.ExecutionException: Boxed Error Caused by: java.lang.Unsatis ...