javaArrayList 源码解析.docx

上传人:b****5 文档编号:4360754 上传时间:2022-11-30 格式:DOCX 页数:13 大小:21.52KB
下载 相关 举报
javaArrayList 源码解析.docx_第1页
第1页 / 共13页
javaArrayList 源码解析.docx_第2页
第2页 / 共13页
javaArrayList 源码解析.docx_第3页
第3页 / 共13页
javaArrayList 源码解析.docx_第4页
第4页 / 共13页
javaArrayList 源码解析.docx_第5页
第5页 / 共13页
点击查看更多>>
下载资源
资源描述

javaArrayList 源码解析.docx

《javaArrayList 源码解析.docx》由会员分享,可在线阅读,更多相关《javaArrayList 源码解析.docx(13页珍藏版)》请在冰豆网上搜索。

javaArrayList 源码解析.docx

javaArrayList源码解析

ArrayList源码解析

——动力节点java

ArrayList是list接口下一个底层用数组实现的典型list类,也就是传说中的动态数组,用MSDN的说法就是array的复杂版本,它提供了动态的增加和减少元素,实现了ICollection和IList接口,灵活的设置数组的大小等好处。

相对于LinkedList来说,ArrayList的特点是查询数据快,增删数据慢。

1、ArrayList的属性及常用方法。

A、两个私有属性

privatetransientObject[]elementData;//表示存储在ArrayList内的元素

privateintsize;//表示它包含元素的数量

B、三种构造器

//带容量大小的构造方法

publicArrayList(intinitialCapacity){

super();

if(initialCapacity<0)

thrownewIllegalArgumentException("IllegalCapacity:

"+initialCapacity);

this.elementData=newObject[initialCapacity];

}

//无参构造方法,默认大小为10

publicArrayList(){

super();

this.elementData=EMPTY_ELEMENTDATA;

}

publicArrayList(Collection

extendsE>c){

//调用toArray()把Collection转换成数组

elementData=c.toArray();

//把数组的长度赋值给ArrayList的size属性

size=elementData.length;

//c.toArraymight(incorrectly)notreturnObject[](see6260652)

if(elementData.getClass()!

=Object[].class)

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

}

第一个构造方法使用提供的initialCapacity来初始化elementDate数组的大小,第二个构造方法调用构造方法并传入参数10,也就是默认elementData数组的大小为10.第三个构造方法将提供的集合转成数组返回给elementData(返回若不是Object[]就调用Aeeays.copyOf()将其转为Object[])。

 

C、ArrayList的动态扩容:

oldcapa*1.5

privatestaticfinalintDEFAULT_CAPACITY=10;

以add方法为入口

publicbooleanadd(Ee){

    ensureCapacityInternal(size+1); //IncrementsmodCount!

!

    elementData[size++]=e;

    returntrue;

  }

可见,在添加元素之前,会先调用ensureCapacityInternal这个方法,那就再进到这个这个方法中去。

 privatevoidensureCapacityInternal(intminCapacity){

    if(elementData==EMPTY_ELEMENTDATA){

      minCapacity=Math.max(DEFAULT_CAPACITY,minCapacity);

    }

    ensureExplicitCapacity(minCapacity);

  }

    首先,看看数组是否为空,如果是,就将DEFAULT_CAPACITY和minCapacity的较大的一个作为初始大小赋值给minCapacity ,DEFAULT_CAPACITY是10,minCapacity就是add方法中传入的size+1。

如果数组不为空,就直接执行ensureExplicitCapacity方法。

ensureExplicitCapacity方法的实现如下:

privatevoidensureExplicitCapacity(intminCapacity){

    modCount++;

    //overflow-consciouscode

    if(minCapacity-elementData.length>0)

      grow(minCapacity);

  }

   在这个方法里,会比较minCapacity与elementData.length的大小。

当第一次插入值时,由于minCapacity一定大于等于10,而elementData.length是0,所以会去继续执行grow方法,那就继续进入到这个方法中去。

grow方法的实现如下:

Privatevoidgrow(intminCapacity){

//overflow-consciouscode

intoldCapacity=elementData.length;

intnewCapacity=oldCapacity+(oldCapacity>>1);

if(newCapacity-minCapacity<0)

newCapacity=minCapacity;

if(newCapacity-MAX_ARRAY_SIZE>0)

newCapacity=hugeCapacity(minCapacity);

//minCapacityisusuallyclosetosize,sothisisawin:

elementData=Arrays.copyOf(elementData,newCapacity);

}

这个方法首先计算出一个容量,大小为oldCapacity+(oldCapacity>>1)。

即elementData数组长度的1.5倍。

再从minCapacity和

这个容量中取较大的值作为扩容后的新的数组的大小。

 这时,会出现两种情况:

1)新的容量小于数组的最大值MAX_ARRAY_SIZE,即

privatestaticfinalintMAX_ARRAY_SIZE=Integer.MAX_VALUE-8;

 最大的容量之所以设为Integer.MAX_VALUE-8,在定义上方的注释中已经说了,大概是一些JVM实现时,会在数组的前面放一些额外的数据,再加上数组中的数据大小,有可能超过一次能申请的整块内存的大小上限,所以会出现OutOfMemoryError。

2)新的容量大于数组的最大值MAX_ARRAY_SIZE 

这时,又会进入另一个方法hugeCapacity()

 privatestaticinthugeCapacity(intminCapacity){

    

if(minCapacity<0)//overflow

      thrownewOutOfMemoryError();

    return(minCapacity>MAX_ARRAY_SIZE)?

      Integer.MAX_VALUE:

      MAX_ARRAY_SIZE;

 }

会对minCapacity和MAX_ARRAY_SIZE进行比较,minCapacity大的话,就将Integer.MAX_VALUE作为新数组的大小,否则将MAX_ARRAY_SIZE作为数组的大小。

最后,就把原来数组的数据复制到新的数组中。

调用了Arrays的copyOf方法。

内部是System的arraycopy方法,由于是native方法,所以效率较高。

  二、ArrayList的其他方法

  add(Ee)

  add(Ee)都知道是在尾部添加一个元素,如何实现的呢?

1.public boolean add(E e) {  

2.    ensureCapacity(size + 1);  // Increments modCount!

!

  

3.    elementData[size++] = e;  

4.    return true;  

5.    }  

1.public boolean add(E e) {  

2.    ensureCapacity(size + 1);  // Increments modCount!

!

  

3.    elementData[size++] = e;  

4.    return true;  

5.    }  

 都说ArrayList是基于数组实现的,属性中也看到了数组,具体是怎么实现的呢?

比如就这个添加元素的方法,如果数组大,则在将某个位置的值设置为指定元素即可,如果数组容量不够了呢?

  看到add(Ee)中先调用了ensureCapacity(size+1)方法,之后将元素的索引赋给elementData[size],而后size自增。

例如初次添加时,size为0,add将elementData[0]赋值为e,然后size设置为1(类似执行以下两条语句elementData[0]=e;size=1)。

将元素的索引赋给elementData[size]不是会出现数组越界的情况吗?

这里关键就在ensureCapacity(size+1)中了。

  

 

根据ensureCapacity的方法名可以知道是确保容量用的。

ensureCapacity(size+1)后面的注释可以明白是增加modCount的值(加了俩感叹号,应该蛮重要的,来看看)。

1./** 

2.      * Increases the capacity of this ArrayList instance, if 

3.      * necessary, to ensure that it can hold at least the number of elements 

4.      * specified by the minimum capacity argument. 

5.      * 

6.      * @param   minCapacity   the desired minimum capacity 

7.      */  

8.     public void ensureCapacity(int minCapacity) {  

9.     modCount++;  

10.     int oldCapacity = elementData.length;  

11.     if (minCapacity > oldCapacity) {  

12.         Object oldData[] = elementData;  

13.         int newCapacity = (oldCapacity * 3)/2 + 1;  

14.             if (newCapacity < minCapacity)  

15.         newCapacity = minCapacity;  

16.             // minCapacity is usually close to size, so this is a win:

  

17.             elementData = Arrays.copyOf(elementData, newCapacity);  

18.     }  

19.     }  

这是对modCount的解释,意为记录list结构被改变的次数(观察源码可以发现每次调用ensureCapacoty方法,modCount的值都将增加,但未必数组结构会改变,所以感觉对modCount的解释不是很到位)。

   增加modCount之后,判断minCapacity(即size+1)是否大于oldCapacity(即elementData.length),若大于,则调整容量为max((oldCapacity*3)/2+1,minCapacity),调整elementData容量为新的容量,即将返回一个内容为原数组元素,大小为新容量的数组赋给elementData;否则不做操作。

  所以调用ensureCapacity至少将elementData的容量增加的1,所以elementData[size]不会出现越界的情况。

   容量的拓展将导致数组元素的复制,多次拓展容量将执行多次整个数组内容的复制。

若提前能大致判断list的长度,调用ensureCapacity调整容量,将有效的提高运行速度。

  可以理解提前分配好空间可以提高运行速度,但是测试发现提高的并不是很大,而且若list原本数据量就不会很大效果将更不明显。

  

 

add(intindex,Eelement)在指定位置插入元素。

1.public void add(int index, E element) {  

2.     if (index > size || index < 0)  

3.         throw new IndexOutOfBoundsException(  

4.         "Index:

 "+index+", Size:

 "+size);  

5.   

6.     ensureCapacity(size+1);  // Increments modCount!

!

  

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

8.              size - index);  

9.     elementData[index] = element;  

10.     size++;  

11.     }    

首先判断指定位置index是否超出elementData的界限,之后调用ensureCapacity调整容量(若容量足够则不会拓展),调用System.arraycopy将elementData从index开始的size-index个元素复制到index+1至size+1的位置(即index开始的元素都向后移动一个位置),然后将index位置的值指向element。

  

  addAll(Collection

extendsE>c)

1.public boolean addAll(Collection

 extends E> c) {  

2.     Object[] a = c.toArray();  

3.         int numNew = a.length;  

4.     ensureCapacity(size + numNew);  // Increments modCount  

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

6.         size += numNew;  

7.     return numNew !

= 0;  

8.     }  

 先将集合c转换成数组,根据转换后数组的程度和ArrayList的size拓展容量,之后调用System.arraycopy方法复制元素到elementData的尾部,调整size。

根据返回的内容分析,只要集合c的大小不为空,即转换后的数组长度不为0则返回true。

  addAll(intindex,Collection

extendsE>c)

1.public boolean addAll(int index, Collection

 extends E> c) {  

2.     if (index > size || index < 0)  

3.         throw new IndexOutOfBoundsException(  

4.         "Index:

 " + index + ", Size:

 " + size);  

5.   

6.     Object[] a = c.toArray();  

7.     int numNew = a.length;  

8.     ensureCapacity(size + numNew);  // Increments modCount  

9.   

10.     int numMoved = size - index;  

11.     if (numMoved > 0)  

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

13.                  numMoved);  

14.   

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

16.     size += numNew;  

17.     return numNew !

= 0;  

18.     }  

 先判断index是否越界。

其他内容与addAll(Collection

extendsE>c)基本一致,只是复制的时候先将index开始的元素向后移动X(c转为数组后的长度)个位置(也是一个复制的过程),之后将数组内容复制到elementData的index位置至index+X。

  clear()

1.public void clear() {  

2.     modCount++;  

3.   

4.     // Let gc do its work  

5.     for (int i = 0; i < size; i++)  

6.         elementData[i] = null;  

7.   

8.     size = 0;  

9.     }  

clear的时候并没有修改elementData的长度(好不容易申请、拓展来的,凭什么释放,留着搞不好还有用呢。

这使得确定不再修改list内容之后最好调用trimToSize来释放掉一些空间),只是将所有元素置为null,size设置为0。

  clone()

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

(不复制这些元素本身。

1.public Object clone() {  

2.     try {  

3.         ArrayList v = (ArrayList) super.clone();  

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

5.         v.modCount = 0;  

6.         return v;  

7.     } catch (CloneNotSupportedException e) {  

8.         // this shouldn't happen, since we are Cloneable  

9.         throw new InternalError();  

10.     }  

11.     }  

 调用父类的clone方法返回一个对象的副本,将返回对象的elementData数组的内容赋值为原对象elementData数组的内容,将副本的modCount设置为0。

  

contains(Object)

1.public boolean contains(Object o) {  

2.     return indexOf(o) >= 0;  

3.     }  

 indexOf方法返回值与0比较来判断对象是否在list中。

接着看indexOf。

  indexOf(Object)

1.public int indexOf(Object o) {  

2.     if (o == null) {  

3.         for (int i = 0; i < size; i++)  

4.         if (elementData[i]==null)  

5.             return i;  

6.     } else {  

7.         for (int i = 0; i < size; i++)  

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

9.             return i;  

10.     }  

11.     return -1;  

12.     }  

通过遍历elementData数组来判断对象是否在list中,若存在,返回index([0,size-1]),若不存在则返回-1。

所以contains方法可以通过indexOf(Object)方法的返回值来判断对象是否被包含在list中。

  既然看了indexOf(Object)方法,接着就看lastIndexOf,光看名字应该就明白了返回的是传入对象在elementData数组中最后出现的index值。

1.public int lastIndexOf(Object o) {  

2.     if (o == null) {  

3.         for (int i = size-1; i >= 0; i--)  

4.         if (elementData[i]==null)  

5.             return i;  

6.     } else {  

7.         for (int i = size-1; i >= 0; i--)  

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

9.             return i;  

10.     }  

采用了从后向前遍历element数组,若遇到Objec

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 小学教育 > 其它课程

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1