ArrayList 源码阅读~

1 引言

首先,我们要对 ArrayList 的特点有一个基本的认识,比如说 :

  • ArrayList 底层是用什么实现的?
  • 缺点是什么?有点又是什么?
  • 扩容机制了解吗

其实 ArrayList 底层是基于数组来实现的,随机读的时间复杂度为 O(1), 对于删除的的时间复杂度为O(N)。所以 ArrayList 的优势在于随机读,缺点有两个地方,一个是不断地添加元素的时候,会扩容,扩容会导致性能较差,另一个删除元素的时候,会导致大量元素的拷贝,性能也较差。

2 继承关系

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable

ArrayList实现了List, RandomAccess, Cloneable, java.io.Serializable等接口。

  • ArrayList实现了List,提供了基础的添加、删除、遍历等操作。
  • ArrayList实现了RandomAccess,提供了随机访问的能力。
  • ArrayList实现了Cloneable,可以被克隆。
  • ArrayList实现了Serializable,可以被序列化。

3 初始化

一共 3 个构造函数

  1. 不指定初始大小,默认为 10
public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
  1. 指定初始大小
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);
        }
    }
  1. 传入指定集合初始化
    public ArrayList(Collection<? extends E> c) {
        Object[] a = c.toArray();
        if ((size = a.length) != 0) {
            if (c.getClass() == ArrayList.class) {
                elementData = a;
            } else {
                elementData = Arrays.copyOf(a, size, Object[].class);
            }
        } else {
            // replace with empty array.
            elementData = EMPTY_ELEMENTDATA;
        }
    }

4 核心方法

4.1 set

public E set(int index, E element) {
        // 判断是否数组越界
        rangeCheck(index);
        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
}

private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

4.2 add

  1. 在末尾插入
	public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
 }
  1. 在指定下标插入
public void add(int index, E element) {
        // 添加时判断数据越界
        rangeCheckForAdd(index);
        ensureCapacityInternal(size + 1);  // Increments modCount!!
  			
  			// 数组拷贝
        System.arraycopy(elementData, index, elementData, index + 1, size - index);
        elementData[index] = element;
        size++;
}

// 添加时判断数据越界
private void rangeCheckForAdd(int index) {
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

4.3 get

直接通过索引下标获取元素

public E get(int index) {
    rangeCheck(index);
    checkForComodification();
    return ArrayList.this.elementData(offset + index);
}

4.4 remove

调用 System.arraycopy() 将 index+1 后面的元素都复制到 index 位置上,该操作的时间复杂度为 O(N)。

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;
}

5 扩容机制

在添加元素的时候,可能会触发扩容机制,新容量的大小为 oldCapacity + (oldCapacity >> 1), 大约是旧容量的 1.5 倍左右。

相关源码如下:

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

private void ensureCapacityInternal(int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    ensureExplicitCapacity(minCapacity);
}

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
  	// 新容量的大小为 oldCapacity + (oldCapacity >> 1)
    // 大约是旧容量的 1.5 倍左右
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
}

6 Fail-Fast

modCount 用来记录 ArrayList 结构发生变化的次数。结构发生变化是指添加或者删除至少一个元素的所有操作,或者是调整内部数组的大小,仅仅只是设置元素的值不算结构发生变化。

在进行序列化或者迭代等操作时,需要比较操作前后 modCount 是否改变,如果改变了需要抛出 ConcurrentModificationException

参考

  1. ArrayList