首页 > 代码库 > 深入理解JAVA集合系列四:ArrayList源码解读

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

在开始本章内容之前,这里先简单介绍下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 = http://www.mamicode.com/new Object[initialCapacity];>
技术分享

这里先定义默认的初始容量为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 = http://www.mamicode.com/Arrays.copyOf(elementData, newCapacity);>
技术分享

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

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

 remove方法解读

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

技术分享
 1  public E remove(int index) {
 2     RangeCheck(index);
 3 
 4     modCount++;
 5     E oldValue = http://www.mamicode.com/(E) elementData[index];>
技术分享

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

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

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

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

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

ArrayList的优缺点

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

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

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

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

 

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