这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 综合技术 » 基础知识 » Java培训之容器类ArrayList源码分析

共2条 1/1 1 跳转至

Java培训之容器类ArrayList源码分析

菜鸟
2020-12-28 14:57:54     打赏

本文是针对Java1.8的源代码进行解析的,可能会和其他版本有所出入。

一、继承和实现

继承:AbstractList

实现:List,RandomAccess,Cloneable,Serializable接口

publicclassArrayListextendsAbstractList
implementsList,RandomAccess,Cloneable,java.io.Serializable{
}


二、全局变量

1.默认容量

privatestaticfinalintDEFAULT_CAPACITY=10;

2.空的对象数组

privatestaticfinalObject[]EMPTY_ELEMENTDATA={};

3.默认的空数组

//无参构造函数创建的数组

privatestaticfinalObject[]DEFAULTCAPACITY_EMPTY_ELEMENTDATA={};

4.存放数据的数组的缓存变量,不可序列化

transientObject[]elementData;

5.数组的大小

privateintsize;

三、构造方法

1.带有容量initialCapacity的构造方法

源码解释:

publicArrayList(intinitialCapacity){

//如果初始化时ArrayList大小大于0

if(initialCapacity>0){

//new一个该大小的object数组赋给elementData

this.elementData=newObject[initialCapacity];

}elseif(initialCapacity==0){//如果大小为0

//将空数组赋给elementData

this.elementData=EMPTY_ELEMENTDATA;

}else{//小于0

//则抛出IllegalArgumentException异常

thrownewIllegalArgumentException("IllegalCapacity:"+

initialCapacity);

}

}

2.不带参数的构造方法

源码解释:

publicArrayList(){

//直接将空数组赋给elementData

this.elementData=DEFAULTCAPACITY_EMPTY_ELEMENTDATA;

}

3.带参数Collection的构造方法

源码解释:

参数c为一个Collection,Collection的实现类大概有以下几种常用类型:

List:元素可以重复的容器

Set:元素不可重复的容器

Queue:结构是一个队列,先进先出

这个构造方法的意思是,将一个Collection实现类的对象转换为一个ArrayList,但是c容器装的内容

必须为ArrayList装的内容的子类。例如,将一个装了String内容的HashSet转换为装了String内容的

ArrayList,使得ArrayList的大小和值数组都是HashSet的大小和值数组。具体实现如下代码,首先调

用c(Collection的具体实现类)的toArray方法,具体大家可以看各个实现类的toArray方法,但是大

概意思都是将c容器转换为object类型的数组,因为它们的返回值都是object[]。之于下面的两个判断

是当得到的elementData的类名不是Object类名的时候或者是长度为0的时候才会执行。

publicArrayList(Collectionc){

elementData=c.toArray();

if((size=elementData.length)!=0){

if(elementData.getClass()!=Object[].class)

elementData=Arrays.copyOf(elementData,size,Object[].class);

}else{

//replacewithemptyarray.

this.elementData=EMPTY_ELEMENTDATA;

}

}

四、方法

1.trimToSize()

说明:将ArrayList的容量设置为当前size的大小。首先需要明确一个概念,ArrayList的size就是ArrayList的元素个数,length是ArrayList申请的内容空间长度。ArrayList每次都会预申请多一点空间,以便添加元素的时候不需要每次都进行扩容操作,例如我们的元素个数是10个,它申请的内存空间必定会大于10,即length>size,而这个方法就是把ArrayList的内存空间设置为size,去除没有用到的null值空间。这也就是我们为什么每次在获取数据长度是都是调用list.size()而不是list.length()。

源码解释:首先modCount是从类java.util.AbstractList继承的字段,这个字段主要是为了防止在多线程操作的情况下,List发生结构性的变化,什么意思呢?就是防止一个线程正在迭代,另外一个线程进行对List进行remove操作,这样当我们迭代到最后一个元素时,很明显此时List的最后一个元素为空,那么这时modCount就会告诉迭代器,让其抛出异常ConcurrentModificationException。

如果没有这一个变量,那么系统肯定会报异常ArrayIndexOutOfBoundsException,这样的异常显然不是应该出现的(这些运行时错误都是使用者的逻辑错误导致的,我们的JDK那么高端,不会出现使用错误,我们只抛出使用者造成的错误,而这个错误是设计者应该考虑的),为了避免出现这样的异常,定义了检查。

(引用自:郭无心,详情可以看他在知乎的回答:https://www.zhihu.com/question/24086463/answer/64717159)。

publicvoidtrimToSize(){

modCount++;

//如果size小于length

if(size<elementData.length){

//重新将elementData设置大小为size

elementData=(size==0)

?EMPTY_ELEMENTDATA

:Arrays.copyOf(elementData,size);

}

}

2.size()

说明:返回ArrayList的大小

源码解释:直接返回size

publicintsize(){

returnsize;

}

3.isEmpty()

说明:返回是否为空

**源码解释:**直接返回判断size==0

publicbooleanisEmpty(){

returnsize==0;

}

4.indexOf(Objecto)

说明:对象o在ArrayList中的下标位置,如果存在返回位置i,不存在返回-1

源码解释:遍历ArrayList的大小,比较o和容器内的元素,若相等,则返回位置i,若遍历完都不相等,返回-1

publicintindexOf(Objecto){

if(o==null){

for(inti=0;i<size;i++)

if(elementData[i]==null)

returni;

}else{

for(inti=0;i<size;i++)

if(o.equals(elementData[i]))

returni;

}

return-1;

}

5.contains(Objecto)

说明:是否包含对象o

源码解释:调用indexOf()方法得到下标,存在则下标>=0,不存在为-1,即只要比较下标和0的大小即可。

publicbooleancontains(Objecto){

returnindexOf(o)>=0;

}

6.lastIndexOf(Objecto)

说明:返回容器内出现o的最后一个位置

源码解释:从后向前遍历,得到第一个出现对象o的位置,不存在则返回-1

publicintlastIndexOf(Objecto){

if(o==null){

for(inti=size-1;i>=0;i--)

if(elementData[i]==null)

returni;

}else{

for(inti=size-1;i>=0;i--)

if(o.equals(elementData[i]))

returni;

}

return-1;

}

7.clone()

说明:返回此ArrayList实例的浅表副本。

源码解释:

publicObjectclone(){

try{

//调用父类(翻看源码可见是Object类)的clone方法得到一个ArrayList副本

ArrayListv=(ArrayList)super.clone();

//调用Arrays类的copyOf,将ArrayList的elementData数组赋值给副本的elementData数组

v.elementData=Arrays.copyOf(elementData,size);

v.modCount=0;

//返回副本v

returnv;

}catch(CloneNotSupportedExceptione){

thrownewInternalError(e);

}

}

8.toArray()

说明:ArrayList实例转换为。

源码解释:直接调用Arrays类的copyOf。

publicObject[]toArray(){

returnArrays.copyOf(elementData,size);

}

9.toArray(T[]a)

说明:将ArrayList里面的元素赋值到一个数组中去

源码解释:如果a的长度小于ArrayList的长度,直接调用Arrays类的copyOf,返回一个比a数组长度要大的新数组,里面元素就是ArrayList里面的元素;如果a的长度比ArrayList的长度大,那么就调用System.arraycopy,将ArrayList的elementData数组赋值到a数组,然后把a数组的size位置赋值为空。

publicT[]toArray(T[]a){

if(a.length<size)

//Makeanewarrayofa'sruntimetype,butmycontents:

return(T[])Arrays.copyOf(elementData,size,a.getClass());

System.arraycopy(elementData,0,a,0,size);

if(a.length>size)

a[size]=null;

returna;

}

10.rangeCheck(intindex)

说明:测试index是否越界

源码解释:

privatevoidrangeCheck(intindex){

//如果下标超过ArrayList的数组长度

if(index>=size)

//抛出IndexOutOfBoundsException异常

thrownewIndexOutOfBoundsException(outOfBoundsMsg(index));

}

11.get(intindex)

说明:获取index位置的元素

源码解释:先检查是否越界,然后返回ArrayList的elementData数组index位置的元素。

publicEget(intindex){

//检查是否越界

rangeCheck(index);

//返回ArrayList的elementData数组index位置的元素

returnelementData(index);

}

12.set(intindex,Eelement)

说明:设置index位置的元素值了element,返回该位置的之前的值

源码解释:

publicEset(intindex,Eelement){

//检查是否越界

rangeCheck(index);

//调用elementData(index)获取到当前位置的值

EoldValue=elementData(index);

//将element赋值到ArrayList的elementData数组的第index位置

elementData[index]=element;

returnoldValue;

}

13.ensureCapacityInternal(intminCapacity)

说明:得到最小扩容量

源码解释:

privatevoidensureCapacityInternal(intminCapacity){

if(elementData==DEFAULTCAPACITY_EMPTY_ELEMENTDATA){

//获取默认的容量和传入参数的较大值

minCapacity=Math.max(DEFAULT_CAPACITY,minCapacity);

}

ensureExplicitCapacity(minCapacity);

}

14.ensureExplicitCapacity(intminCapacity)

说明:判断是否需要扩容

源码解释:

privatevoidensureExplicitCapacity(intminCapacity){

modCount++;

//如果最小需要空间比elementData的内存空间要大,则需要扩容

if(minCapacity-elementData.length>0)

grow(minCapacity);

}

15.grow()方法

说明:帮助ArrayList动态扩容的核心方法

源码解释:

//MAX_VALUE为231-1,MAX_ARRAY_SIZE就是获取Java中int的最大限制,以防止越界

privatestaticfinalintMAX_ARRAY_SIZE=Integer.MAX_VALUE-8;

privatevoidgrow(intminCapacity){

//获取到ArrayList中elementData数组的内存空间长度

intoldCapacity=elementData.length;

//扩容至原来的1.5倍

intnewCapacity=oldCapacity+(oldCapacity>>1);

//再判断一下新数组的容量够不够,够了就直接使用这个长度创建新数组,

//不够就将数组长度设置为需要的长度

if(newCapacity-minCapacity<0)

newCapacity=minCapacity;

//判断有没超过最大限制

if(newCapacity-MAX_ARRAY_SIZE>0)

newCapacity=hugeCapacity(minCapacity);

//调用Arrays.copyOf方法将elementData数组指向新的内存空间时newCapacity的连续空间

//并将elementData的数据复制到新的内存空间

elementData=Arrays.copyOf(elementData,newCapacity);

}

16.add(Ee)

说明:添加元素e

源码解释:

publicbooleanadd(Ee){

//扩容

ensureCapacityInternal(size+1);

//将e赋值给elementData的size+1的位置。

elementData[size++]=e;

returntrue;

}

17.add(intindex,Eelement)

说明:在ArrayList的index位置,添加元素element

源码解释:

publicvoidadd(intindex,Eelement){

//判断index是否越界

rangeCheckForAdd(index);

//扩容

ensureCapacityInternal(size+1);

//将elementData从index位置开始,复制到elementData的index+1开始的连续空间

System.arraycopy(elementData,index,elementData,index+1,

size-index);

//在elementData的index位置赋值element

elementData[index]=element;

//ArrayList的大小加一

size++;

}

18.remove(intindex)

说明:在ArrayList的移除index位置的元素

源码解释:

publicEremove(intindex){

//判断是否越界

rangeCheck(index);

modCount++;

//读取旧值

EoldValue=elementData(index);

//获取index位置开始到最后一个位置的个数

intnumMoved=size-index-1;

if(numMoved>0)

//将elementData数组index+1位置开始拷贝到elementData从index开始的空间

System.arraycopy(elementData,index+1,elementData,index,

numMoved);

//使size-1,设置elementData的size位置为空,让GC来清理内存空间

elementData[--size]=null;//cleartoletGCdoitswork

returnoldValue;

}

19.remove(Objecto)

说明:在ArrayList的移除对象为O的元素,跟indexOf方法思想基本一致

源码解释:

publicbooleanremove(Objecto){

if(o==null){

for(intindex=0;index<size;index++)

if(elementData[index]==null){

fastRemove(index);

returntrue;

}

}else{

for(intindex=0;index<size;index++)

if(o.equals(elementData[index])){

fastRemove(index);

returntrue;

}

}

returnfalse;

}

20.clear()

说明:设置全部元素为null值,并设置size为0。

源码解释:可见clear操作并不是从空间内删除,只是设置为null值,等待垃圾回收机制来回收而已,把size设置为0,以便我们不会浏览到null值的内存空间。

publicvoidclear(){

modCount++;

//cleartoletGCdoitswork

for(inti=0;i<size;i++)

elementData[i]=null;

size=0;

}

21.addAll(Collectionc)

说明:将Collectionc的全部元素添加到ArrayList中

源码解释:

publicbooleanaddAll(Collectionc){

//将c转换为数组a

Object[]a=c.toArray();

//获取a占的内存空间长度赋值给numNew

intnumNew=a.length;

//扩容至size+numNew

ensureCapacityInternal(size+numNew);//IncrementsmodCount

//将a的第0位开始拷贝至elementData的size位开始,拷贝长度为numNew

System.arraycopy(a,0,elementData,size,numNew);

//将size增加numNew

size+=numNew;

//如果c为空,返回false,c不为空,返回true

returnnumNew!=0;

}

22.addAll(intindex,Collectionc)

说明:从第index位开始,将c全部拷贝到ArrayList

源码解释:

publicbooleanaddAll(intindex,Collectionc){

//判断index大于size或者是小于0,如果是,则抛出IndexOutOfBoundsException异常

rangeCheckForAdd(index);

//将c转换为数组a

Object[]a=c.toArray();

intnumNew=a.length;

//扩容至size+numNew

ensureCapacityInternal(size+numNew);//IncrementsmodCount

//获取需要添加的个数

intnumMoved=size-index;

if(numMoved>0)

System.arraycopy(elementData,index,elementData,index+numNew,

numMoved);

System.arraycopy(a,0,elementData,index,numNew);

size+=numNew;

returnnumNew!=0;

}

24.batchRemove(Collectionc,booleancomplement)

说明:根据complement值,将ArrayList中包含c中元素的元素删除或者保留

源码解释:

privatebooleanbatchRemove(Collectionc,booleancomplement){

finalObject[]elementData=this.elementData;

//定义一个w,一个r,两个同时右移

intr=0,w=0;

booleanmodified=false;

try{

//r先右移

for(;r<size;r++)

//如果c中不包含elementData[r]这个元素

if(c.contains(elementData[r])==complement)

//则直接将r位置的元素赋值给w位置的元素,w自增

elementData[w++]=elementData[r];

}finally{

//防止抛出异常导致上面r的右移过程没完成

if(r!=size){

//将r未右移完成的位置的元素赋值给w右边位置的元素

System.arraycopy(elementData,r,

elementData,w,

size-r);

//修改w值增加size-r

w+=size-r;

}

if(w!=size){

//如果有被覆盖掉的元素,则将w后面的元素都赋值为null

for(inti=w;i<size;i++)

elementData[i]=null;

modCount+=size-w;

//修改size为w

size=w;

modified=true;

}

}

returnmodified;

}

25.removeAll(Collectionc)

说明:ArrayList移除c中的所有元素

源码解释:

publicbooleanremoveAll(Collectionc){

//如果c为空,则抛出空指针异常

Objects.requireNonNull(c);

//调用batchRemove移除c中的元素

returnbatchRemove(c,false);

}

26.retainAll(Collectionc)

说明:和removeAll相反,仅保留c中所有的元素

源码解释:

publicbooleanretainAll(Collectionc){

Objects.requireNonNull(c);

//调用batchRemove保留c中的元素

returnbatchRemove(c,true);

}

27.iterator()

说明:返回一个Iterator对象,Itr为ArrayList的一个内部类,其实现了Iterator接口

publicIteratoriterator(){

returnnewItr();

}

28.listIterator()

说明:返回一个ListIterator对象,ListItr为ArrayList的一个内部类,其实现了ListIterator接口

源码解释:

publicListIteratorlistIterator(){

returnnewListItr(0);

}

29.listIterator(intindex)

说明:返回一个从index开始的ListIterator对象

源码解释:

publicListIteratorlistIterator(intindex){

if(index<0||index>size)

thrownewIndexOutOfBoundsException("Index:"+index);

returnnewListItr(index);

}

30.subList(intfromIndex,inttoIndex)

说明:根据两个参数,获取到一个子序列

源码解释:

publicListsubList(intfromIndex,inttoIndex){

//检查异常

subListRangeCheck(fromIndex,toIndex,size);

//调用SubList类的构造方法

returnnewSubList(this,0,fromIndex,toIndex);

}

五、内部类

(1)privateclassItrimplementsIterator

(2)privateclassListItrextendsItrimplementsListIterator

(3)privateclassSubListextendsAbstractListimplementsRandomAccess

(4)staticfinalclassArrayListSpliteratorimplementsSpliterator

ArrayList有四个内部类,

其中的Itr是实现了Iterator接口,同时重写了里面的hasNext(),next(),remove()等方法;

其中的ListItr继承Itr,实现了ListIterator接口,同时重写了hasPrevious(),nextIndex(),

previousIndex(),previous(),set(Ee),add(Ee)等方法,所以这也可以看出了Iterator和ListIterator的区别,就是ListIterator在Iterator的基础上增加了添加对象,修改对象,逆向遍历等方法,这些是Iterator不能实现的。具体可以参考http://blog.csdn.net/a597926661/article/details/7679765。

其中的SubList继承AbstractList,实现了RandmAccess接口,类内部实现了对子序列的增删改查等方法,但它同时也充分利用了内部类的优点,就是共享ArrayList的全局变量,例如检查器变量modCount,数组elementData等,所以SubList进行的增删改查操作都是对ArrayList的数组进行的,并没有创建新的数组。
本文是针对Java1.8的源代码进行解析的,可能会和其他版本有所出入。

一、继承和实现

继承:AbstractList

实现:List,RandomAccess,Cloneable,Serializable接口

源代码

publicclassArrayListextendsAbstractList

implementsList,RandomAccess,Cloneable,java.io.Serializable{

}

二、全局变量

1.默认容量

privatestaticfinalintDEFAULT_CAPACITY=10;

2.空的对象数组

privatestaticfinalObject[]EMPTY_ELEMENTDATA={};

3.默认的空数组

//无参构造函数创建的数组

privatestaticfinalObject[]DEFAULTCAPACITY_EMPTY_ELEMENTDATA={};

4.存放数据的数组的缓存变量,不可序列化

transientObject[]elementData;

5.数组的大小

privateintsize;

三、构造方法

1.带有容量initialCapacity的构造方法

源码解释:

publicArrayList(intinitialCapacity){

//如果初始化时ArrayList大小大于0

if(initialCapacity>0){

//new一个该大小的object数组赋给elementData

this.elementData=newObject[initialCapacity];

}elseif(initialCapacity==0){//如果大小为0

//将空数组赋给elementData

this.elementData=EMPTY_ELEMENTDATA;

}else{//小于0

//则抛出IllegalArgumentException异常

thrownewIllegalArgumentException("IllegalCapacity:"+

initialCapacity);

}

}

2.不带参数的构造方法

源码解释:

publicArrayList(){

//直接将空数组赋给elementData

this.elementData=DEFAULTCAPACITY_EMPTY_ELEMENTDATA;

}

3.带参数Collection的构造方法

源码解释:

参数c为一个Collection,Collection的实现类大概有以下几种常用类型:

List:元素可以重复的容器

Set:元素不可重复的容器

Queue:结构是一个队列,先进先出

这个构造方法的意思是,将一个Collection实现类的对象转换为一个ArrayList,但是c容器装的内容

必须为ArrayList装的内容的子类。例如,将一个装了String内容的HashSet转换为装了String内容的

ArrayList,使得ArrayList的大小和值数组都是HashSet的大小和值数组。具体实现如下代码,首先调

用c(Collection的具体实现类)的toArray方法,具体大家可以看各个实现类的toArray方法,但是大

概意思都是将c容器转换为object类型的数组,因为它们的返回值都是object[]。之于下面的两个判断

是当得到的elementData的类名不是Object类名的时候或者是长度为0的时候才会执行。

publicArrayList(Collectionc){

elementData=c.toArray();

if((size=elementData.length)!=0){

if(elementData.getClass()!=Object[].class)

elementData=Arrays.copyOf(elementData,size,Object[].class);

}else{

//replacewithemptyarray.

this.elementData=EMPTY_ELEMENTDATA;

}

}

四、方法

1.trimToSize()

说明:将ArrayList的容量设置为当前size的大小。首先需要明确一个概念,ArrayList的size就是ArrayList的元素个数,length是ArrayList申请的内容空间长度。ArrayList每次都会预申请多一点空间,以便添加元素的时候不需要每次都进行扩容操作,例如我们的元素个数是10个,它申请的内存空间必定会大于10,即length>size,而这个方法就是把ArrayList的内存空间设置为size,去除没有用到的null值空间。这也就是我们为什么每次在获取数据长度是都是调用list.size()而不是list.length()。

源码解释:首先modCount是从类java.util.AbstractList继承的字段,这个字段主要是为了防止在多线程操作的情况下,List发生结构性的变化,什么意思呢?就是防止一个线程正在迭代,另外一个线程进行对List进行remove操作,这样当我们迭代到最后一个元素时,很明显此时List的最后一个元素为空,那么这时modCount就会告诉迭代器,让其抛出异常ConcurrentModificationException。

如果没有这一个变量,那么系统肯定会报异常ArrayIndexOutOfBoundsException,这样的异常显然不是应该出现的(这些运行时错误都是使用者的逻辑错误导致的,我们的JDK那么高端,不会出现使用错误,我们只抛出使用者造成的错误,而这个错误是设计者应该考虑的),为了避免出现这样的异常,定义了检查。

(引用自:郭无心,详情可以看他在知乎的回答:https://www.zhihu.com/question/24086463/answer/64717159)。

publicvoidtrimToSize(){

modCount++;

//如果size小于length

if(size<elementData.length){

//重新将elementData设置大小为size

elementData=(size==0)

?EMPTY_ELEMENTDATA

:Arrays.copyOf(elementData,size);

}

}

2.size()

说明:返回ArrayList的大小

源码解释:直接返回size

publicintsize(){

returnsize;

}

3.isEmpty()

说明:返回是否为空

**源码解释:**直接返回判断size==0

publicbooleanisEmpty(){

returnsize==0;

}

4.indexOf(Objecto)

说明:对象o在ArrayList中的下标位置,如果存在返回位置i,不存在返回-1

源码解释:遍历ArrayList的大小,比较o和容器内的元素,若相等,则返回位置i,若遍历完都不相等,返回-1

publicintindexOf(Objecto){

if(o==null){

for(inti=0;i<size;i++)

if(elementData[i]==null)

returni;

}else{

for(inti=0;i<size;i++)

if(o.equals(elementData[i]))

returni;

}

return-1;

}

5.contains(Objecto)

说明:是否包含对象o

源码解释:调用indexOf()方法得到下标,存在则下标>=0,不存在为-1,即只要比较下标和0的大小即可。

publicbooleancontains(Objecto){

returnindexOf(o)>=0;

}

6.lastIndexOf(Objecto)

说明:返回容器内出现o的最后一个位置

源码解释:从后向前遍历,得到第一个出现对象o的位置,不存在则返回-1

publicintlastIndexOf(Objecto){

if(o==null){

for(inti=size-1;i>=0;i--)

if(elementData[i]==null)

returni;

}else{

for(inti=size-1;i>=0;i--)

if(o.equals(elementData[i]))

returni;

}

return-1;

}

7.clone()

说明:返回此ArrayList实例的浅表副本。

源码解释:

publicObjectclone(){

try{

//调用父类(翻看源码可见是Object类)的clone方法得到一个ArrayList副本

ArrayListv=(ArrayList)super.clone();

//调用Arrays类的copyOf,将ArrayList的elementData数组赋值给副本的elementData数组

v.elementData=Arrays.copyOf(elementData,size);

v.modCount=0;

//返回副本v

returnv;

}catch(CloneNotSupportedExceptione){

thrownewInternalError(e);

}

}

8.toArray()

说明:ArrayList实例转换为。

源码解释:直接调用Arrays类的copyOf。

publicObject[]toArray(){

returnArrays.copyOf(elementData,size);

}

9.toArray(T[]a)

说明:将ArrayList里面的元素赋值到一个数组中去

源码解释:如果a的长度小于ArrayList的长度,直接调用Arrays类的copyOf,返回一个比a数组长度要大的新数组,里面元素就是ArrayList里面的元素;如果a的长度比ArrayList的长度大,那么就调用System.arraycopy,将ArrayList的elementData数组赋值到a数组,然后把a数组的size位置赋值为空。

publicT[]toArray(T[]a){

if(a.length<size)

//Makeanewarrayofa'sruntimetype,butmycontents:

return(T[])Arrays.copyOf(elementData,size,a.getClass());

System.arraycopy(elementData,0,a,0,size);

if(a.length>size)

a[size]=null;

returna;

}

10.rangeCheck(intindex)

说明:测试index是否越界

源码解释:

privatevoidrangeCheck(intindex){

//如果下标超过ArrayList的数组长度

if(index>=size)

//抛出IndexOutOfBoundsException异常

thrownewIndexOutOfBoundsException(outOfBoundsMsg(index));

}

11.get(intindex)

说明:获取index位置的元素

源码解释:先检查是否越界,然后返回ArrayList的elementData数组index位置的元素。

publicEget(intindex){

//检查是否越界

rangeCheck(index);

//返回ArrayList的elementData数组index位置的元素

returnelementData(index);

}

12.set(intindex,Eelement)

说明:设置index位置的元素值了element,返回该位置的之前的值

源码解释:

publicEset(intindex,Eelement){

//检查是否越界

rangeCheck(index);

//调用elementData(index)获取到当前位置的值

EoldValue=elementData(index);

//将element赋值到ArrayList的elementData数组的第index位置

elementData[index]=element;

returnoldValue;

}

13.ensureCapacityInternal(intminCapacity)

说明:得到最小扩容量

源码解释:

privatevoidensureCapacityInternal(intminCapacity){

if(elementData==DEFAULTCAPACITY_EMPTY_ELEMENTDATA){

//获取默认的容量和传入参数的较大值

minCapacity=Math.max(DEFAULT_CAPACITY,minCapacity);

}

ensureExplicitCapacity(minCapacity);

}

14.ensureExplicitCapacity(intminCapacity)

说明:判断是否需要扩容

源码解释:

privatevoidensureExplicitCapacity(intminCapacity){

modCount++;

//如果最小需要空间比elementData的内存空间要大,则需要扩容

if(minCapacity-elementData.length>0)

grow(minCapacity);

}

15.grow()方法

说明:帮助ArrayList动态扩容的核心方法

源码解释:

//MAX_VALUE为231-1,MAX_ARRAY_SIZE就是获取Java中int的最大限制,以防止越界

privatestaticfinalintMAX_ARRAY_SIZE=Integer.MAX_VALUE-8;

privatevoidgrow(intminCapacity){

//获取到ArrayList中elementData数组的内存空间长度

intoldCapacity=elementData.length;

//扩容至原来的1.5倍

intnewCapacity=oldCapacity+(oldCapacity>>1);

//再判断一下新数组的容量够不够,够了就直接使用这个长度创建新数组,

//不够就将数组长度设置为需要的长度

if(newCapacity-minCapacity<0)

newCapacity=minCapacity;

//判断有没超过最大限制

if(newCapacity-MAX_ARRAY_SIZE>0)

newCapacity=hugeCapacity(minCapacity);

//调用Arrays.copyOf方法将elementData数组指向新的内存空间时newCapacity的连续空间

//并将elementData的数据复制到新的内存空间

elementData=Arrays.copyOf(elementData,newCapacity);

}

16.add(Ee)

说明:添加元素e

源码解释:

publicbooleanadd(Ee){

//扩容

ensureCapacityInternal(size+1);

//将e赋值给elementData的size+1的位置。

elementData[size++]=e;

returntrue;

}

17.add(intindex,Eelement)

说明:在ArrayList的index位置,添加元素element

源码解释:

publicvoidadd(intindex,Eelement){

//判断index是否越界

rangeCheckForAdd(index);

//扩容

ensureCapacityInternal(size+1);

//将elementData从index位置开始,复制到elementData的index+1开始的连续空间

System.arraycopy(elementData,index,elementData,index+1,

size-index);

//在elementData的index位置赋值element

elementData[index]=element;

//ArrayList的大小加一

size++;

}

18.remove(intindex)

说明:在ArrayList的移除index位置的元素

源码解释:

publicEremove(intindex){

//判断是否越界

rangeCheck(index);

modCount++;

//读取旧值

EoldValue=elementData(index);

//获取index位置开始到最后一个位置的个数

intnumMoved=size-index-1;

if(numMoved>0)

//将elementData数组index+1位置开始拷贝到elementData从index开始的空间

System.arraycopy(elementData,index+1,elementData,index,

numMoved);

//使size-1,设置elementData的size位置为空,让GC来清理内存空间

elementData[--size]=null;//cleartoletGCdoitswork

returnoldValue;

}

19.remove(Objecto)

说明:在ArrayList的移除对象为O的元素,跟indexOf方法思想基本一致

源码解释:

publicbooleanremove(Objecto){

if(o==null){

for(intindex=0;index<size;index++)

if(elementData[index]==null){

fastRemove(index);

returntrue;

}

}else{

for(intindex=0;index<size;index++)

if(o.equals(elementData[index])){

fastRemove(index);

returntrue;

}

}

returnfalse;

}

20.clear()

说明:设置全部元素为null值,并设置size为0。

源码解释:可见clear操作并不是从空间内删除,只是设置为null值,等待垃圾回收机制来回收而已,把size设置为0,以便我们不会浏览到null值的内存空间。

publicvoidclear(){

modCount++;

//cleartoletGCdoitswork

for(inti=0;i<size;i++)

elementData[i]=null;

size=0;

}

21.addAll(Collectionc)

说明:将Collectionc的全部元素添加到ArrayList中

源码解释:

publicbooleanaddAll(Collectionc){

//将c转换为数组a

Object[]a=c.toArray();

//获取a占的内存空间长度赋值给numNew

intnumNew=a.length;

//扩容至size+numNew

ensureCapacityInternal(size+numNew);//IncrementsmodCount

//将a的第0位开始拷贝至elementData的size位开始,拷贝长度为numNew

System.arraycopy(a,0,elementData,size,numNew);

//将size增加numNew

size+=numNew;

//如果c为空,返回false,c不为空,返回true

returnnumNew!=0;

}

22.addAll(intindex,Collectionc)

说明:从第index位开始,将c全部拷贝到ArrayList

源码解释:

publicbooleanaddAll(intindex,Collectionc){

//判断index大于size或者是小于0,如果是,则抛出IndexOutOfBoundsException异常

rangeCheckForAdd(index);

//将c转换为数组a

Object[]a=c.toArray();

intnumNew=a.length;

//扩容至size+numNew

ensureCapacityInternal(size+numNew);//IncrementsmodCount

//获取需要添加的个数

intnumMoved=size-index;

if(numMoved>0)

System.arraycopy(elementData,index,elementData,index+numNew,

numMoved);

System.arraycopy(a,0,elementData,index,numNew);

size+=numNew;

returnnumNew!=0;

}

24.batchRemove(Collectionc,booleancomplement)

说明:根据complement值,将ArrayList中包含c中元素的元素删除或者保留

源码解释:

privatebooleanbatchRemove(Collectionc,booleancomplement){

finalObject[]elementData=this.elementData;

//定义一个w,一个r,两个同时右移

intr=0,w=0;

booleanmodified=false;

try{

//r先右移

for(;r<size;r++)

//如果c中不包含elementData[r]这个元素

if(c.contains(elementData[r])==complement)

//则直接将r位置的元素赋值给w位置的元素,w自增

elementData[w++]=elementData[r];

}finally{

//防止抛出异常导致上面r的右移过程没完成

if(r!=size){

//将r未右移完成的位置的元素赋值给w右边位置的元素

System.arraycopy(elementData,r,

elementData,w,

size-r);

//修改w值增加size-r

w+=size-r;

}

if(w!=size){

//如果有被覆盖掉的元素,则将w后面的元素都赋值为null

for(inti=w;i<size;i++)

elementData[i]=null;

modCount+=size-w;

//修改size为w

size=w;

modified=true;

}

}

returnmodified;

}

25.removeAll(Collectionc)

说明:ArrayList移除c中的所有元素

源码解释:

publicbooleanremoveAll(Collectionc){

//如果c为空,则抛出空指针异常

Objects.requireNonNull(c);

//调用batchRemove移除c中的元素

returnbatchRemove(c,false);

}

26.retainAll(Collectionc)

说明:和removeAll相反,仅保留c中所有的元素

源码解释:

publicbooleanretainAll(Collectionc){

Objects.requireNonNull(c);

//调用batchRemove保留c中的元素

returnbatchRemove(c,true);

}

27.iterator()

说明:返回一个Iterator对象,Itr为ArrayList的一个内部类,其实现了Iterator接口

publicIteratoriterator(){

returnnewItr();

}

28.listIterator()

说明:返回一个ListIterator对象,ListItr为ArrayList的一个内部类,其实现了ListIterator接口

源码解释:

publicListIteratorlistIterator(){

returnnewListItr(0);

}

29.listIterator(intindex)

说明:返回一个从index开始的ListIterator对象

源码解释:

publicListIteratorlistIterator(intindex){

if(index<0||index>size)

thrownewIndexOutOfBoundsException("Index:"+index);

returnnewListItr(index);

}

30.subList(intfromIndex,inttoIndex)

说明:根据两个参数,获取到一个子序列

源码解释:

publicListsubList(intfromIndex,inttoIndex){

//检查异常

subListRangeCheck(fromIndex,toIndex,size);

//调用SubList类的构造方法

returnnewSubList(this,0,fromIndex,toIndex);

}

五、内部类

(1)privateclassItrimplementsIterator

(2)privateclassListItrextendsItrimplementsListIterator

(3)privateclassSubListextendsAbstractListimplementsRandomAccess

(4)staticfinalclassArrayListSpliteratorimplementsSpliterator

ArrayList有四个内部类,

其中的Itr是实现了Iterator接口,同时重写了里面的hasNext(),next(),remove()等方法;

其中的ListItr继承Itr,实现了ListIterator接口,同时重写了hasPrevious(),nextIndex(),

previousIndex(),previous(),set(Ee),add(Ee)等方法,所以这也可以看出了Iterator和ListIterator的区别,就是ListIterator在Iterator的基础上增加了添加对象,修改对象,逆向遍历等方法,这些是Iterator不能实现的。具体可以参考http://blog.csdn.net/a597926661/article/details/7679765。

其中的SubList继承AbstractList,实现了RandmAccess接口,类内部实现了对子序列的增删改查等方法,但它同时也充分利用了内部类的优点,就是共享ArrayList的全局变量,例如检查器变量modCount,数组elementData等,所以SubList进行的增删改查操作都是对ArrayList的数组进行的,并没有创建新的数组。


以上就是关于Java培训之容器类ArrayList源码分析的详细介绍,最后想要了解更多关于JavaEE开发问题的小伙伴可以登录扣丁学堂官网咨询。扣丁学堂是专业的JavaEE培训机构,不仅有专业的老师和与时俱进的课程体系,还有大量的Java视频教程供学员观看学习,想要学好JavaEE的小伙伴抓紧时间行动吧。扣丁学堂java技术交流群:487098661。微信号:codingbb



工程师
2020-12-28 22:48:01     打赏
2楼

不错的分享


共2条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]