正文
ArrayList 在我们的代码中相信一定是很常见的,今天就来看一看 ArrayList 的源码 (不包含 Spliterator 相关的, Spliterator 会在后面单独讲一下),这次博客主要内容直接写在代码注释里面
来看一下 ArrayList 的相关变量
 private static final long serialVersionUID = 8683452581122892189L;
    // todo 默认的大小
    private static final int DEFAULT_CAPACITY = 10;
    // todo 这个空数组用于构造器中传入初始容量 = 0
    private static final Object[] EMPTY_ELEMENTDATA = {};
//   todo  这个数组用于默认构造器,(会使用默认的容量 10)
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//   todo 真实的数组元素
    transient Object[] elementData; // non-private to simplify nested class access
//   todo 数组大小
    private int size;
    
    // todo 最大数组容量 (这里实际上最大的还是 Integer.MAX_VALUE, 为了保证某些虚拟机会在数组中存放一些头信息,所以 -8)
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
看到变量里面有两个空的数组,用处是不一样的,一个是构造器传入容量为 0 的时候使用,另一个是默认构造器初始化使用,默认构造器是有默认容量的,为 10,当第一次使用,发现是默认构造器初始化的就会初始化数组长度为 10
看一下下面三个构造器
// 带初始容量的构造器,可以看到,当传入的容量为 0 的时候,会使用 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);
        }
    }
// 默认的构造器,无参数,直接将数组元素设置为 DEFAULTCAPACITY_EMPTY_ELEMENTDATA  
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
// 将另一个集合复制进来的构造器,使用了 System.copyOf 函数,如果集合是空的,那么就使用 EMPTY_ELEMENTDATA   
    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;
        }
    }    
add 和 set 方法
    public boolean add(E e) {
//        todo 确保容量,不足就扩容并且会增加 modCount,在迭代过程中,不允许 add 操作
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
    
    public void add(int index, E element) {
//        todo 确保 index 合法, index <= size && index >= 0
        rangeCheckForAdd(index);
        ensureCapacityInternal(size + 1);  // Increments modCount!!
//        todo 调用 arrayCopy 方法法复制 index 后面的元素向后移动一位
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }
    
    // set 方法并不会改变 modCount, 所以在使用迭代器迭代的时候,可以通过 set 修改数据(涉及到数组长度的操作都会修改 modCount)
    public E set(int index, E element) {
        rangeCheck(index);
//        todo 对数组制定下表的元素重新赋值,返回老元素,不会改变迭代器 modCount 状态
        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }
在上面的 add 方法中,有一个 ensureCapacityInternal 方法,该方法主要用于扩容的判断
    private void ensureCapacityInternal(int minCapacity) {
        // todo 这里就会使用到,当构造器不传和传 0 处理方式是不一样的,如果是空构造,就会使用 DEFAULT_CAPACITY 和 minCapacity 中大的作为初始容量
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        ensureExplicitCapacity(minCapacity);
    }
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        // overflow-conscious code
        // todo 如果 minCap 大于当前的长度,就需要扩容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
    private void grow(int minCapacity) {
        // overflow-conscious code
//        todo 记录老的长度
        int oldCapacity = elementData.length;
//        todo newCapacity = 1.5 * oldCapacity
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        // 我认为这里是越界了
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
//        todo 如果大于 MAX_ARRAY_SIZE (Integer.MAX - 8),就会使用 Integer.MAX
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        // todo 调用 copy 函数
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
    
再来看一下 get 方法,get 方法比较简单,范围判断后直接返回对应数组下标
    E elementData(int index) {
//        todo 直接返回数组的第 index 个元素,不会改变 modCount 状态
        return (E) elementData[index];
    }
    public E get(int index) {
    // 范围检查
        rangeCheck(index);
        return elementData(index);
    }
toArray 方法
     public Object[] toArray() {
//        todo 返回一个新的数组
        return Arrays.copyOf(elementData, size);
     }
    @SuppressWarnings("unchecked")
    public <T> T[] toArray(T[] a) {
//        todo 如果传进来的数组长度太小,就会使用新建一个数组
        if (a.length < size)
            // Make a new array of a's runtime type, but my contents:
            return (T[]) Arrays.copyOf(elementData, size, a.getClass());
//        todo 长度够,就在传进来的数组上赋值
        System.arraycopy(elementData, 0, a, 0, size);
        if (a.length > size)
            a[size] = null;
        return a;
    }
和删除相关的代码 remove fastRemove clear
  public E remove(int index) {
        rangeCheck(index);
        modCount++;
        E oldValue = elementData(index);
//        todo 计算需要移动的元素个数
        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;
    }
    public boolean remove(Object o) {
//        todo null 要特殊处理
        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;
    }
 
//    todo 和 remove(index) 功能一样
    private void fastRemove(int index) {
        modCount++;
        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
    }
    public void clear() {
//      todo 清空所有元素,并且 size = 0;
        modCount++;
        // clear to let GC do its work
        for (int i = 0; i < size; i++)
            elementData[i] = null;
        size = 0;
    }
    
    // 范围移除
    protected void removeRange(int fromIndex, int toIndex) {
        modCount++;
        // 需要移动的元素个数
        int numMoved = size - toIndex;
        System.arraycopy(elementData, toIndex, elementData, fromIndex,
                         numMoved);
        // clear to let GC do its work
        int newSize = size - (toIndex-fromIndex);
        for (int i = newSize; i < size; i++) {
            elementData[i] = null;
        }
        size = newSize;
    }    
看一下 ArrayList 的迭代器
    private class Itr implements Iterator<E> {
        int cursor;       // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount;
        // 通过游标是否到达 size 来判断有没有下一个元素
        public boolean hasNext() {
            return cursor != size;
        }
        @SuppressWarnings("unchecked")
        public E next() {
        // 判断 modCount 是否被修改,被修改就会抛出异常
            checkForComodification();
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
                // 游标+1
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }
        // todo 在迭代器中要使用迭代器中的 remove 方法
        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();
            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }
        @Override
        @SuppressWarnings("unchecked")
        public void forEachRemaining(Consumer<? super E> consumer) {
            Objects.requireNonNull(consumer);
            final int size = ArrayList.this.size;
            int i = cursor;
            if (i >= size) {
                return;
            }
            final Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length) {
                throw new ConcurrentModificationException();
            }
            while (i != size && modCount == expectedModCount) {
                consumer.accept((E) elementData[i++]);
            }
            // update once at end of iteration to reduce heap write traffic
            cursor = i;
            lastRet = i - 1;
            checkForComodification();
        }
        final void checkForComodification() {
            // todo 这里会检查 遍历中的 modCount 和期望的是否一致,不一致会抛出异常
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }
本文首次发布于 fyypumpkin Blog, 作者 @fyypumpkin ,转载请保留原文链接.
