1. 简介

不管是做Android开发还是做Java开发,都会经常使用到ArrayList 。ArrayList底层基于数组实现容量大小动态变化。下面将介绍ArrayList的使用以及分析ArrayList的源码,从而对ArrayList有一个深入的理解。

2. ArrayList的使用

先看以下实例:

package zzw.cn.listtest;

import java.util.ArrayList;

/**
 * @author 鹭岛猥琐男
 * @create 2019/8/6 20:56
 */
public class TestArrayList
{
    public static void main(String[] args)
    {
        //创建ArrayList
        ArrayList<Integer> arrayList = new ArrayList<>();
        for (int i = 0; i < 15; i++)
        {
            arrayList.add(i);//将数据添加进ArrayList
        }
        System.out.println("遍历ArrayList:");
        for (int i = 0; i < arrayList.size(); i++)
        {
            //取出ArrayList中的数据
            if (i != arrayList.size() - 1)
            {
                System.out.print(arrayList.get(i) + ",");
            } else
            {
                System.out.print(arrayList.get(i));
            }
        }
    }
}

结果:

遍历ArrayList:
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14
Process finished with exit code 0

以上实例包含ArrayList的创建,数据的添加,以及从ArrayList中读取数据。

ArrayList常用的API有如下:

Modifier and Type

Method and Description

boolean

add(E

将指定的元素追加到此列表的末尾。

void

add(int index, E

在此列表中的指定位置插入指定的元素。

boolean

addAll(Collection<? extends E> c)

按指定集合的Iterator返回的顺序将指定集合中的所有元素追加到此列表的末尾。

boolean

addAll(int index, Collection<? extends E> c)

将指定集合中的所有元素插入到此列表中,从指定的位置开始。

void

clear()

从列表中删除所有元素。

E

get(int index)

返回此列表中指定位置的元素。

boolean

isEmpty()

如果此列表不包含元素,则返回 true 。

E

remove(int index)

删除该列表中指定位置的元素。

boolean

remove(Object

从列表中删除指定元素的第一个出现(如果存在)。

int

size()

返回此列表中的元素数。

3. 源码分析 

3.1 构造方法
ArrayList有三个构造方法:

Android ByteArray初始化 android arraylist用法_ArrayList

第一个构造方法:无参构造方法

public ArrayList() { 
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

在此方法中, elementData 是一个Object类型的数组,并且对其赋值一个空的Object类型数组。

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};


    transient Object[] elementData; // non-private to simplify nested class access

第二个构造方法:不常用。

public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

此构造方法中, 先将传入的参数(集合)转化为数组,并赋值给 elementData 。当 elementData 长度不为空时,如果不是Object[].class类型,那么久需要使用ArrayList中的方法去做转化。当 elementData 长度为空时,给 elementData 赋值一个空的Object类型的数组:

private static final Object[] EMPTY_ELEMENTDATA = {};

第三个构造方法:

public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

当传入的参数 initialCapacity 大于0,则给 elementData 赋一个大小为 initialCapacity 的 Object 类型数组。当 initialCapacity 等于0,则给 elementData 赋一个Object类型的空数组。当 initialCapacity 小于0 ,则直接抛出一个异常。

3.2 add 方法

在ArrayList中增加元素的时候,会使用add函数,总共有4种方法:

Android ByteArray初始化 android arraylist用法_ArrayList_02

这里先分析 add(E e)

此方法直接添加数据元素到arraylist的尾部。

public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

在此方法中,先调用 ensureCapacityInternal(size + 1),把当前列表的长度加1后当做参数 minCapacity,传入 ensureCapacityInternal() 方法中:

private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

在此方法中,首先将 minCapacity 和 elementData 作为参数传入方法 calculateCapacity() 中:

private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

此方法主要的功能是:

如果 elementData 是空数组,则返回 minCapacity 和 DEFAULT_CAPACITY(10) 中的最大值。如果 elementData不是空数组,则返回 minCapacity,并当做参数传入 ensureExplicitCapacity() 方法中:

private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

首先 modCount 增加1,然后,当传入的值比数组 elementData 的长度还长的时候,执行方法 grow(minCapacity):

private void grow(int minCapacity) {
        // 将elementData数组的长度作为原容量
        int oldCapacity = elementData.length;
        // 扩容为原来的1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            // 若1.5倍扩容后还不够,则将最小容量作为新容量
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            // 限制最大容量
            newCapacity = hugeCapacity(minCapacity);
        // 进行原有数据元素copy处理
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

在grow() 方法中,首先把数组 elementData 的长度当做原容量,并对其进行扩容1.5倍作为新容量。如果扩容1.5倍后的容量还不够,则将新容量的值更改为传入的最小容量(minCapacity)。接着判断通过调用方法 hugeCapacity() 限制新容量的最大值。最后,通过调用 Arrays.copyOf 实现对数组 elementData 的扩容。Arrays.copyOf 在这里就进行介绍,想了解相关的知识,请移步此篇blog:System.arrayCopy()与Arrays.copyOf()的区别。

继续回到 add(E e) 方法中,以上的步骤实现了对数组 elementData 的扩容后,接着调用了 elementData[size++] = e ,对数组进行了赋值操作,并返回 true,表示数据添加成功。

接下来继续分析add(int index, E element)

此方法的作用是将元素插入到ArrayList中的指定位置。

public void add(int index, E element) {
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

在这个方法中,首先先判断要插入的位置index 是否超出ArrayList的长度,以及是否小于0,如果是,则抛出数组下标越界的异常(IndexOutOfBoundsException)。接着调用方法 ensureCapacityInternal(size + 1),对内部数组 elementData 进行扩容操作。当数组扩容结束后,调用 System.arraycopy() 将指定位置 index 后的元素后移一位。想了解 System.arraycopy() 的相关知识,请移步此篇blog:System.arrayCopy()与Arrays.copyOf()的区别。最后对指定位置index的元素进行赋值操作并将size自增1。

3.3 remove 方法

在ArrayList中,删除单个元素有以下两种方法,下面将分别介绍:

Android ByteArray初始化 android arraylist用法_ArrayList_03

remove(int index) :

用于删除该列表中指定位置的元素。

public E remove(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

        modCount++;
        E oldValue = (E) elementData[index];

        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }

先判断需要删除的下标是否超过ArrayList的长度,如果超过,则抛出数组下标越界的异常(IndexOutOfBoundsException);接着读取指定下标位置的数据,并赋值给 oldValue ;然后计算需要移动的数据个数 numMoved ,当需要移动的个数大于0的时候,调用方法System.arraycopy(),这样指定下标后的元素都会往前移动一位;最后将代表ArrayList大小的size值减一,并将数组elementData 的最后一位置为null,并且返回删除的数据。

remove(Object o):

用于从列表中删除第一个出现的指定元素(如果存在)。

public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

当调用此方法的时候,会把需要删除的 Object 类型的数据 o 分为空和非空进行处理。然后对数组 elementData 进行遍历处理,当 o 为null 的时候,如果遍历到数组 elementData 中第一个数据为 null,则找到对应的下标;当 o 不为 null 的时候,如果遍历到数组 elementData 中第一个数据为 o,则找到对应的下边。接着调用 fastRemove(index),把下标index当做参数传入方法 fastRemove()。而方法 fastRemove(int index) 和 remove(int index) 的逻辑基本相同。

3.4 get 方法

get 方法只有一种,用于返回此列表中指定位置的元素。

Android ByteArray初始化 android arraylist用法_ci_04

public E get(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

        return (E) elementData[index];
    }

 首先进行越界判断,接着直接返回数组 elementData 中下标为 index 的数据元素。

4. 总结

ArrayList本质上就是一个 Object 类型的数组,能存放null值。ArrayList的默认初始化容量是10,每次扩容时候增加原先容量的一半,也就是变为原来的1.5倍。由于本质是个数组,所以随机存取快,但是在增删时候,需要数组的拷贝复制,会比较耗费性能。从以上的add和remove的分析,可以看出ArrayList不是线程安全的,如果需要考虑线程的安全性,推荐用Vector,内部代码实现大部分都是一样的,只不过vector的方法都加上了synchronized锁来保证线程安全。