深入理解JAVA集合系列四:ArrayList源码解读

java

在开始本章内容之前,这里先简单介绍下List的相关内容。

List的简单介绍

有序的collection,用户可以对列表中每个元素的插入位置进行精确的控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。列表通常允许重复的元素,且允许null元素的存放。

ArrayList的简单介绍

JDK中这样定义ArrayList:List接口的大小可变数据的实现。

主要有以下特点:

1、有序

2、线程不安全

3、元素可以重复

4、可以存放null值

顾名思义,取名ArrayList是因为其内部是用一个大小可变的数组存储元素,也就是说底层是一个动态数组。

add方法解读

List<String> li = new ArrayList<String>();

li.add("33");

先看这个构造函数:

 public ArrayList() {

this(10);

}

1  public ArrayList(int initialCapacity) {

2 super();

3 if (initialCapacity < 0)

4 throw new IllegalArgumentException("Illegal Capacity: "+

5 initialCapacity);

6 this.elementData = new Object[initialCapacity];

7 }

这里先定义默认的初始容量为10,然后构建了一个容量为10的数组elementData;这个elementData数组就是ArrayList底层存放元素的数组,且元素是Object类型。该数组的源码定义也了解下:

private transient Object[] elementData;

再来看ADD方法:

 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 }

1、第6行的扩容部分后面再解释,其余的比较简单,我们直接看第7行。调用的是System.arraycopy的方法。

2、第一次看到这个方法有点搞不懂,先来解释下:System.arraycopy(src, srcPos, dest, destPos, length);

     第一个参数表示的是要被复制的数组;第二个参数表示数组中从第几个元素开始复制。

     第三个参数表示复制到哪个数组;第四个参数表示复制到那个数组从哪个元素开始;第五个参数表示要复制的长度

3、解释完这个方法就很清楚了,add方法就是将数组中待添加元素的位置后面所有的元素(包括该位置),通过copy的复制方法,全部后移一个位置。然后将待添加的元素放到指      定的位置上。当然这个数组可能是原数组,也可能是经过第6行代码扩容之后新的数组。

扩容

扩容,动态扩容,也就是前面所提到的ArrayList的底层是由一个动态变化的数组实现的。

 1 public void ensureCapacity(int minCapacity) {

2 modCount++;

3 int oldCapacity = elementData.length;

4 if (minCapacity > oldCapacity) {

5 Object oldData[] = elementData;

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

7 if (newCapacity < minCapacity)

8 newCapacity = minCapacity;

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

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

11 }

12 }

1、我们看扩容的算法,也就是在旧容量的基础上,扩大1.5倍再加上1。至于为什么是这样一个算法,我也没明白。但是JDK开发人员肯定是在这方面做过思考的:不能一次性扩容太大,会造成内存空间的浪费;也不能扩容得不够,否则下一次扩容的操作会很快到来。我猜想应该是基于这样的考虑才设计了这样一个算法。

2、扩容结束后,就是将旧数组里面的元素全部复制到新的数组里。并且新数组还是通过elementData变量来引用。

 remove方法解读

我们先来看下该方法的源码:

 1  public E remove(int index) {

2 RangeCheck(index);

3

4 modCount++;

5 E oldValue = (E) elementData[index];

6

7 int numMoved = size - index - 1;

8 if (numMoved > 0)

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

10 numMoved);

11 elementData[--size] = null; // Let gc do its work

12

13 return oldValue;

14 }

其实这个remove的方法处理的过程也比较简单:

1、第2行代码是用来检测数组下标是否越界。

2、然后取出该位置上的数组元素。

3、通过arryCopy方法将待删除元素之后的所有元素往前一个位置复制。(注意,这个动作是先复制,然后粘贴)。

4、这个时候最后一个位置上的元素就是多余的,所以才有了第11行的处理,将其置为null。

ArrayList的优缺点

1、底层是由数组实现的,便于随机访问。

2、顺序添加元素性能比较高,相当于每次在数组最后添加一个元素、

3、插入元素、删除元素的时候,会涉及到元素的复制,即调用arrayCopy方法,如果复制的元素比较多,则这样就比较消耗性能。

总结来说:ArrayList的优势是随机访问、顺序添加。

以上是 深入理解JAVA集合系列四:ArrayList源码解读 的全部内容, 来源链接: utcz.com/z/393171.html

回到顶部