ArrayList的源码解析

首先,来总览一下 ArrayList 的类结构图。
ArrayList的类结构图

接着,我们来看一下 ArrayList 的属性:

private static final int DEFAULT_CAPACITY = 10; // 初始化容量为10

private static final Object[] EMPTY_ELEMENTDATA = {}; // 指定该ArrayList容量为0时,返回该空数组

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; // 没有指定容量时,默认返回该数组

transient Object[] elementData; // 保存添加到ArrayList中的元素(当第一次添加元素时,将扩容到 DEFAULT_CAPACITY)

private int size; // ArrayList实际的大小

根据上面我们可以清晰的发现:ArrayList 底层其实就是一个数组,ArrayList 中有扩容这么一个概念,正因为它扩容,所以它能够实现动态增长。

一、构造方法

public ArrayList(int initialCapacity) {
    // 如果指定了容量,那么数组就初始化成对应的容量
    if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        // 容量为0,则将 elementData 指向事先定义好,容量为0数组
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}

public ArrayList() {
    // 不传入参数,则将 elementData 指向默认容量为10的数组
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

二、add 方法

2.1 add(E e)

首先,我们来看一下这个方法:

public boolean add(E e) {
    // 确认list容量,尝试容量加1,看看是否有必要扩容
    ensureCapacityInternal(size + 1); 
    // 添加元素
    elementData[size++] = e;
    return true;
}

接下来,我们来看看这个小容量(+1)是否满足我们的需求:

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

private static int calculateCapacity(Object[] elementData, int minCapacity) {
    // 想要得到最小的容量(不浪费资源)
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}

随后调用 ensureExplicitCapacity() 来确定明确的容量:

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

    if (minCapacity - elementData.length > 0)
        // 如果需要的最小容量比现在数组的容量还要大,那么就需要调用grow()来扩容了
        grow(minCapacity);
}

所以,接下来看看 grow() 是怎么实现的:

private void grow(int minCapacity) {

    int oldCapacity = elementData.length;
    // 相当于扩容1.5倍
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        // 扩完容,如果还是小于minCapacity
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // 调用copyOf()将原数组复制到新数组中
    elementData = Arrays.copyOf(elementData, newCapacity);
}

目前为止,我们已经知道 add(E e) 的基本实现了:首先检查 elementData 数组容量是否足够,如果足够,则直接添加;如果不足,则需要扩容到原来的 1.5 倍(如果第一次扩容后数组容量还是小于 minCapacity,就将容量扩充为 minCapacity)

2.2 add(int index, E element)
public void add(int index, E element) {
    rangeCheckForAdd(index);// 检查角标是否越界

    ensureCapacityInternal(size + 1);// 确认是否需要扩容

    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index); // 插入元素
    elementData[index] = element;
    size++;
}

三、get 方法

public E get(int index) {

    rangeCheck(index);// 检查角标

    return elementData(index);// 返回元素
}
private void rangeCheck(int index) {
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

E elementData(int index) {
    return (E) elementData[index];
}

四、set 方法

public E set(int index, E element) {
    rangeCheck(index);

    E oldValue = elementData(index);// 将值进行替代,返回旧值
    elementData[index] = element;
    return oldValue;
}

五、remove 方法

public E remove(int index) {
    rangeCheck(index); // 检查角标

    modCount++;
    E oldValue = 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 是基于动态数组实现的,在增删的时候,需要数组的拷贝复制
  • ArrayList 的默认初始容量是10,每次扩容的时候增加原容量的一半,也就是变为原来的 1.5 倍
  • 删除元素时不会减少容量,若希望减少容量则调用 trimToSize()
  • 它不是线程安全的。它能存放 null 值
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值