jdk version:1.8
成员变量
public class ArrayList<E> extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable{private static final long serialVersionUID = 8683452581122892189L;/*** 默认的容量*/private static final int DEFAULT_CAPACITY = 10;/*** 初始化 ArrayList 的时候,指定的容量为 0 时,指定的默认空容器*/private static final Object[] EMPTY_ELEMENTDATA = {};/*** 通过无参构造创建 ArrayList 时,指定的默认空容器,和EMPTY_ELEMENTDATA 区分开来是用来标记当添加第一个* 元素的时候,应该膨胀的容量是多少,最少 DEFAULT_CAPACITY 个,详情可见 calculateCapacity 方法。*/private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};/*** 缓存元素的数组*/transient Object[] elementData; // non-private to simplify nested class access/*** 记录元素个数* @serial*/private int size;}
关键字 transient 是表示序列化的时候这个字段不被序列化 transient
构造方法
public ArrayList(int initialCapacity) {if (initialCapacity > 0) {//直接创建一个新数组this.elementData = new Object[initialCapacity];} else if (initialCapacity == 0) {//指定初始化容器数量是 0this.elementData = EMPTY_ELEMENTDATA;} else {throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);}}/*** 无参构造,指定 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 为默认的空数组*/public ArrayList() {this.elementData = DEFAULTCAPACITY_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 {// 原数组的数量为 0,也是默认等于 EMPTY_ELEMENTDATAthis.elementData = EMPTY_ELEMENTDATA;}}
增、改
add、set、adllAll
public E set(int index, E element) {//检测 index 是否不比 size 小,判断条件: if (index >= size)//如果是则抛出 IndexOutOfBoundsExceptionrangeCheck(index);//将新值覆盖到旧值上,并将原来的值返回出去E oldValue = elementData(index);elementData[index] = element;return oldValue;}public boolean add(E e) {//扩容ensureCapacityInternal(size + 1); // Increments modCount!!//在末尾添加元素,并且 size+1elementData[size++] = e;return true;}public void add(int index, E element) {//检测 index 是否不符合条件,判断条件: if (index > size || index < 0)//如果不符合则抛出 IndexOutOfBoundsExceptionrangeCheckForAdd(index);//扩容ensureCapacityInternal(size + 1); // Increments modCount!!//将index 以及右边的元素往后移动一位System.arraycopy(elementData, index, elementData, index + 1,size - index);//为 index 下标附上新的值elementData[index] = element;size++;}public boolean addAll(Collection<? extends E> c) {Object[] a = c.toArray();int numNew = a.length;//扩容ensureCapacityInternal(size + numNew); // Increments modCount//直接从 size 下标开始进行复制赋值System.arraycopy(a, 0, elementData, size, numNew);size += numNew;return numNew != 0;}public boolean addAll(int index, Collection<? extends E> c) {//检测 indexrangeCheckForAdd(index);Object[] a = c.toArray();int numNew = a.length;//扩容ensureCapacityInternal(size + numNew); // Increments modCountint numMoved = size - index;if (numMoved > 0)//numMoved >0 说明要移动原来的数据//在这里,由于 rangeCheckForAdd 检测了 index,所以 numMoved 只可能会>0 或者 ==0.//==0时,该方法和 addAll(Collection<? extends E> c) 没什么区别。System.arraycopy(elementData, index, elementData, index + numNew,numMoved);System.arraycopy(a, 0, elementData, index, numNew);size += numNew;return numNew != 0;}
ensureCapacityInternal
里面总共涉及到四个关键的方法
private void ensureCapacityInternal(int minCapacity) {//先计算容量,再去扩容ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));}//计算容量private static int calculateCapacity(Object[] elementData, int minCapacity) {//从这里可以看出,如果是通过无参构造创建的 ArrayList,那么第一次添加数据扩容的时候,容量最小为 10if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {return Math.max(DEFAULT_CAPACITY, minCapacity);}return minCapacity;}private void ensureExplicitCapacity(int minCapacity) {modCount++;// overflow-conscious code//如果要扩充到的容量比现有的容量要大,才开始真正的扩容if (minCapacity - elementData.length > 0)grow(minCapacity);}private void grow(int minCapacity) {// overflow-conscious codeint oldCapacity = elementData.length;//增加 50%的容量int newCapacity = oldCapacity + (oldCapacity >> 1);if (newCapacity - minCapacity < 0)//增加 50%后,还比需要的容量小,则新容量就等于所需要的容量。newCapacity = minCapacity;if (newCapacity - MAX_ARRAY_SIZE > 0)//如果计算出的新容量比最大值还要大,这里的 MAX_ARRAY_SIZE 为 Integer.MAX_VALUE - 8//则根据所需容量 minCapacity 来计算新容量newCapacity = hugeCapacity(minCapacity);//扩容elementData = Arrays.copyOf(elementData, newCapacity);}private static int hugeCapacity(int minCapacity) {if (minCapacity < 0) // overflowthrow new OutOfMemoryError();//最大极限容量就是 Integer.MAX_VALUEreturn (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE;}
可以看出每次扩容最小都是增加原来的 50%,扩容的最大极限值就是 Integer.MAX_VALUE,如果最后一次扩容量不大于 MAX_ARRAY_SIZE,则直接扩容到 MAX_ARRAY_SIZE ,否则扩容到 Integer.MAX_VALUE。
查
get
public E get(int index) {rangeCheck(index);return elementData(index);}@SuppressWarnings("unchecked")E elementData(int index) {return (E) elementData[index];}
读取元素没啥好说的,检测 index 是否合法,然后直接返回数据。
删
remove、clear
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 workreturn oldValue;}public boolean remove(Object o) {if (o == null) {//如果移除的是 null,则遍历数组,将数组中第一个 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;}private void fastRemove(int index) {modCount++;int numMoved = size - index - 1;//如果移除的数据不是最后一个,则将下标右边的数据往左移一位,然后将最后一个数置为 nullif (numMoved > 0)System.arraycopy(elementData, index+1, elementData, index,numMoved);elementData[--size] = null; // clear to let GC do its work}protected void removeRange(int fromIndex, int toIndex) {//这个方法的移除是包左不包右的 [fromIndex,toIndex) toIndex 最大可以为 sizemodCount++;int numMoved = size - toIndex;System.arraycopy(elementData, toIndex, elementData, fromIndex,numMoved);// clear to let GC do its workint newSize = size - (toIndex-fromIndex);for (int i = newSize; i < size; i++) {elementData[i] = null;}size = newSize;}public void clear() {modCount++;// clear to let GC do its workfor (int i = 0; i < size; i++)elementData[i] = null;size = 0;}
其它方法
trimToSize
将 ArrayList 中的数组容量大小调整为实际元素的个数。
public void trimToSize() {modCount++;if (size < elementData.length) {elementData = (size == 0)? EMPTY_ELEMENTDATA: Arrays.copyOf(elementData, size);}}
toArray
ArrayList 转换为 数组
public Object[] toArray() {//直接返回一个新数组return Arrays.copyOf(elementData, size);}@SuppressWarnings("unchecked")public <T> T[] toArray(T[] a) {if (a.length < size)// Make a new array of a's runtime type, but my contents:return (T[]) Arrays.copyOf(elementData, size, a.getClass());System.arraycopy(elementData, 0, a, 0, size);if (a.length > size)a[size] = null;return a;}
对于第二个方法来说如果传入的是数组长度小于 ArrayList 的长度,则返回一个新数组。如果大于等于 ArrayList 长度,则在传入的数组中进行赋值。size 的下标为 null,后面的数字不变。
ArrayList<Integer> list1 = new ArrayList<>();list1.add(1);list1.add(2);list1.add(3);list1.add(4);list1.add(5);Integer[] a = new Integer[]{6,7,8,9,10,11,12,13,14};Integer[] integers = list1.toArray(a);System.out.println(Arrays.toString(integers));//------------------------ 输出结果 ------------------------[1, 2, 3, 4, 5, null, 12, 13, 14]
Arrays.copyof() 和 System.arraycopy()
Arrays.copyof() 有很多重载的方法,不过最终都调用了一个方法:
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {@SuppressWarnings("unchecked")T[] copy = ((Object)newType == (Object)Object[].class)? (T[]) new Object[newLength]: (T[]) Array.newInstance(newType.getComponentType(), newLength);System.arraycopy(original, 0, copy, 0,Math.min(original.length, newLength));return copy;}
首先创建一个长度为 newLength 的新数组,然后里面最终调用了 System.arraycopy 方法将原来数组中的元素赋值到新数组中,最后将新数组返回。
和 Vector 比较
- Vector 是线程安全的,增删改查几个方法都加入了 synchronized 关键字。
- Vector 的扩容量可以自定义,默认是 2 倍,ArrayList 是增加 50%。
- Vector提供indexOf(obj, start)接口,ArrayList没有。(indexOf(obj, start) 接口是从指定下标 start 开始查找 obj 所处的下标)
Vector 的无参构造是刚开始就创建一个 10 容量的数组;ArrayList 是在添加元素的时候才开始检测如果是通过无参构造创建的,容量最小指定为 10。
总结
ArrayList 底层是数组结构,在增删操作上,如果不是最后一个元素,都会进行数组的复制操作,耗时较久。但是在查询方面可以直接通过下标索引查找到指定的元素,查询速度快。
- ArrayList 通过无参构造创建时,默认是一个容量为 0 的空数组,在后续操作需要扩容的时候,如果发现是通过无参构造创建的,那么扩容的最小容量为 10。(对于 jdk 1.8以前的源码里面是直接创建一个 10 容量的初始数组,这点和 Vector 一样)
EMPTY_ELEMENTDATA和DEFAULT_CAPACITY都是空数组,EMPTY_ELEMENTDATA代表ArrayList是通过无参构造创建的,DEFAULT_CAPACITY代表后续操作ArrayList为空时所指定的数组。两者区分开来,就是为了第一次扩容时,确保容量大小最小为 10。- ArrayList 允许元素为 null。
