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 ...
 
随机推荐
- 虚拟机vmware的连接方式以及IP端口,协议等概念
			
1.NAT虚拟机相当于小弟,宿主机相当于大哥,宿主机虚拟出一个网段供虚拟机上网用 2.Bridge桥接,虚拟机和宿主机相当于局域网中的两台机器 3.Host-Only虚拟机只和宿主机通信,无法上网 3 ...
 - 第十一天 unittest参数化模块
			
unittest的工作原理:一下例子简单的描述了整个过程 def calc(a,b): return a//b import unittest,HTMLTestRunner import Beauti ...
 - 一个关于STL list使用 小示例
			
#include <list> #include <string> using namespace std; typedef struct DiskInfo_st { int ...
 - 2018蓝桥杯C/C++组第4题第几个幸运数
			
题目4标题:第几个幸运数 到x星球旅行的游客都被发给一个整数,作为游客编号.x星的国王有个怪癖,他只喜欢数字3,5和7.国王规定,游客的编号如果只含有因子:3,5,7,就可以获得一份奖品. 我们来看前 ...
 - 牛客训练41D最小相似度bfs
			
最小相似度 题目大意:对于位数相同的两个二进制串,SIM(A,B)为它们的相似度,也就是A^B中0的个数.现在给定一堆串,找出一个T使得max{SIM(S1,T),SIM(S2,T),......,S ...
 - codevs   2010 求后序遍历x
			
题目描述 Description 输入一棵二叉树的先序和中序遍历序列,输出其后序遍历序列. 输入描述 Input Description 共两行,第一行一个字符串,表示树的先序遍历,第二行一个字符串, ...
 - 「美团 CodeM 资格赛」试题泛做
			
LibreOJ真是吼啊! 数码 推个式子,把枚举因数转为枚举倍数.然后就发现它是根号分段的.然后每一段算一下就好了. #include <cstdio> #include <cstr ...
 - BZOJ刷题列表【转载于hzwer】
			
沿着黄学长的步伐~~ 红色为已刷,黑色为未刷,看我多久能搞完吧... Update on 7.26 :之前咕了好久...(足见博主的flag是多么emmm......)这几天开始会抽时间刷的,每天几道 ...
 - 邻居子系统 之 更新neigh_update
			
概述 neigh_update函数用来更新指定的邻居项,更新内容是硬件地址和状态,更新之后,会根据新状态设置其输出函数,CONNECTED状态则使用快速输出,否则使用慢速输出:如果是由原来的无效状态变 ...
 - 2018-2019-2 网络对抗技术 20165205 Exp8 Web基础
			
2018-2019-2 网络对抗技术 20165205 Exp8 Web基础 1.原理与实践说明 1.1实践内容 Web前段HTML:能正常安装.启停Apache.理解HTML,理解表单,理解GET与 ...