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) {
//指定初始化容器数量是 0
this.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_ELEMENTDATA
this.elementData = EMPTY_ELEMENTDATA;
}
}
增、改
add、set、adllAll
public E set(int index, E element) {
//检测 index 是否不比 size 小,判断条件: if (index >= size)
//如果是则抛出 IndexOutOfBoundsException
rangeCheck(index);
//将新值覆盖到旧值上,并将原来的值返回出去
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
public boolean add(E e) {
//扩容
ensureCapacityInternal(size + 1); // Increments modCount!!
//在末尾添加元素,并且 size+1
elementData[size++] = e;
return true;
}
public void add(int index, E element) {
//检测 index 是否不符合条件,判断条件: if (index > size || index < 0)
//如果不符合则抛出 IndexOutOfBoundsException
rangeCheckForAdd(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) {
//检测 index
rangeCheckForAdd(index);
Object[] a = c.toArray();
int numNew = a.length;
//扩容
ensureCapacityInternal(size + numNew); // Increments modCount
int 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,那么第一次添加数据扩容的时候,容量最小为 10
if (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 code
int 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) // overflow
throw new OutOfMemoryError();
//最大极限容量就是 Integer.MAX_VALUE
return (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 work
return 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;
//如果移除的数据不是最后一个,则将下标右边的数据往左移一位,然后将最后一个数置为 null
if (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 最大可以为 size
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;
}
public void clear() {
modCount++;
// clear to let GC do its work
for (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。