注:以下源码均为JDK8的源码
一、 核心属性
基本属性如下:
核心的属性其实是红框中的两个:
//从注释也容易看出,一个是集合元素,一个是集合长度(注意是逻辑长度,即元素的个数,而非数组长度)
其中:transient指明序列化时请忽略。
二、构造器
一共有3个构造器:
1.构造指定容量的ArrayList
2.默认构造器
//可以看到,默认初始容量为10(基本属性中的DEFAULT_CAPACITY)
而 DEFAULTCAPACITY_EMPTY_ELEMENTDATA是一个空数组,所有默认构造器初始化的都指向它,目的是为了防止我们创建过多的无用的List
如果创建ArrayList时用的是无参构造器,则第一次插入时会进行一次扩容并且扩到默认数组大小10
3.使用collection参数的构造器
//接收一个集合进行构造,按照迭代器返回的顺序排列
更多容器初始化的介绍,参考是清浅池塘知乎专栏:https://zhuanlan.zhihu.com/p/27873515
三、添加元素
一共有5个(包含2个重载方法)
//由于是基于数组进行实现的,我们只要抓住它的特点,就能读懂总体的思路
1.set(int,E),使用新元素,替代指定位置旧元素
//比较容易读懂的一个方法:检查下标->取得旧元素->替代->返回新元素
2.add(E)/add(int,E),包含两个重载方法:在末尾添加元素与在指定位置添加元素
//先检查容量是否需要扩容(扩容算法不在这里展开),再在结尾添加元素(size+1)
//同样的,涉及下标的都先检查下标,之后检查是否需要扩容,之后,由于是数组,先右移Index+1的元素,再添加
3.addAll(),两个addAll方法,与上面类似,一个为在末尾添加,一个在指定位置添加,顺序均为迭代器返回的顺序,这里就不展开:
/** * Appends all of the elements in the specified collection to the end of * this list, in the order that they are returned by the * specified collection's Iterator. The behavior of this operation is * undefined if the specified collection is modified while the operation * is in progress. (This implies that the behavior of this call is * undefined if the specified collection is this list, and this * list is nonempty.) * * @param c collection containing elements to be added to this list * @return <tt>true</tt> if this list changed as a result of the call * @throws NullPointerException if the specified collection is null */ public boolean addAll(Collection<? extends E> c) { Object[] a = c.toArray(); int numNew = a.length; ensureCapacityInternal(size + numNew); // Increments modCount System.arraycopy(a, 0, elementData, size, numNew); size += numNew; return numNew != 0; } /** * Inserts all of the elements in the specified collection into this * list, starting at the specified position. Shifts the element * currently at that position (if any) and any subsequent elements to * the right (increases their indices). The new elements will appear * in the list in the order that they are returned by the * specified collection's iterator. * * @param index index at which to insert the first element from the * specified collection * @param c collection containing elements to be added to this list * @return <tt>true</tt> if this list changed as a result of the call * @throws IndexOutOfBoundsException {@inheritDoc} * @throws NullPointerException if the specified collection is null */ public boolean addAll(int index, Collection<? extends E> c) { rangeCheckForAdd(index); Object[] a = c.toArray(); int numNew = a.length; ensureCapacityInternal(size + numNew); // Increments modCount int numMoved = size - index; if (numMoved > 0) System.arraycopy(elementData, index, elementData, index + numNew, numMoved); System.arraycopy(a, 0, elementData, index, numNew); size += numNew; return numNew != 0; }
四、获取元素
一个我们常见的get方法:
//比较简单的下标检查,获取元素
五、删除元素
3个基本的删除方法:
1.remove(int)/remove(Object),两个重载方法,分别是按照下标和按照元素删除:
//范围检查->修改次数modCount加1->得到将要删除的元素,被删除元素后的元素向前进一位,末尾元素置空。
关于modCount:
ArrayList也采用了快速失败的机制,通过记录modCount参数来实现。在面对并发的修改时,迭代器很快就会完全失败,而不是冒着在将来某个不确定时间发生任意不确定行为的风险。
在使用迭代器遍历的时候,如果使用ArrayList中的remove(int index) remove(Object o) remove(int fromIndex ,int toIndex) add等方法的时候都会修改modCount,在迭代的时候需要保持单线程的唯一操作,如果期间进行了插入或者删除,就会被迭代器检查获知,从而出现运行时异常
//先检查要删除的元素在不在,存在则删除返回true,不存在返回false
2.removeRange(int,int),按照范围删除,实际是将elementData从toIndex位置开始的元素向前移动到fromIndex,然后将toIndex位置之后的元素全部置空顺便修改size。这里就不展开频率使用不高的范围删除方法了:
/** * Removes from this list all of the elements whose index is between * {@code fromIndex}, inclusive, and {@code toIndex}, exclusive. * Shifts any succeeding elements to the left (reduces their index). * This call shortens the list by {@code (toIndex - fromIndex)} elements. * (If {@code toIndex==fromIndex}, this operation has no effect.) * * @throws IndexOutOfBoundsException if {@code fromIndex} or * {@code toIndex} is out of range * ({@code fromIndex < 0 || * fromIndex >= size() || * toIndex > size() || * toIndex < fromIndex}) */ 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; }
清空元素也比较容易看懂:
//元素置Null与size置0
在ArrayList中,底层数组存/取元素效率非常的高(get/set),时间复杂度是O(1),而查找,插入和删除元素效率不高,时间复杂度为O(n)。
并且由源码也知道,频繁地插入删除会导致底层数组地复制,这个应当由 LinkedList来弥补它地补足了。