简介:
ArrayList是动态数组,是java集合框架collection中list接口的一个实现类,继承于AbstractList。底层数据结构为一个Object数组。如下,其实现了List、RandomAccess、Cloneable、Serializable接口。
ArrayList继承于AbstractList,实现了list接口,提供了增删改查,遍历等功能。
ArrayList实现了java.io.Serializable接口,表示其可序列化。
ArrayList实现了RandomAccess接口,这是一个标记接口,表明实现这个接口的List集合支持快速随机访问,查找时可以通过下标快速获取相应元素对象,时间复杂度为O(1)。与之相对的LinkedList没有实现这个接口。所以ArrayList查找元素快,插入、删除慢,LinkedList底层为双向链表,链表插入删除速度很快,只需更改其前后节点的指向即可,ArrayList在增删时对数组元素进行下标的挪动。这是两种list的性能差距。
ArrayList实现了Cloneable接口,表示其可克隆,并覆盖了clone方法。
ArrayList是非线程安全的,与之对应的Vector是线程安全的,两者无本质区别,Vector中会在其大部分方法中添加synchronized以确保线程安全。在多线程环境中应避免使用ArrayList,可使用vector或使用JUC里面的CopyOnWriteArrayList。
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
属性:
//默认容量为10
private static final int DEFAULT_CAPACITY = 10;
//static final常量,空数组,长度为0
private static final Object[] EMPTY_ELEMENTDATA = {};
//无参构造时会默认使用这个数组实例,与EMPTY_ELEMENTDATA区分开,以了解添加第一个元素时的膨胀程度
//空数组,会在ensureCapacity中进行判断,若使用此常量对elementData进行赋值,
//则会将elementData的扩容量至少设置为10。
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//存储数据的数组,transient关键字表示在已序列化的类里,该变量不可序列化
transient Object[] elementData; // non-private to simplify nested class access
//数组所包含元素个数
private int size;
transient说明:有了transient
关键字声明,则这个变量不会参与序列化操作,即使所在类实现了Serializable接口,反序列化后该变量为空值。
在参与网络传输时,ArrayList
在序列化的时候会调用writeObject()
方法,将size
和element
写入ObjectOutputStream
;反序列化时调用readObject()
,从ObjectInputStream
获取size
和element
,再恢复到elementData
。
至于为什么不直接用elementData来序列化,原因在于elementData
是一个缓存数组,它通常会预留一些容量,等容量不足时再扩充容量,那么有些空间可能就没有实际存储元素,采用上诉的方式来实现序列化时,就可以保证只序列化实际存储的那些元素,而不是整个数组,从而节省空间和时间。
构造方法:
构造方法很简单,这里只在注释中进行说明。
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
//按传入容量进行初始化
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
使用默认实例赋值
this.elementData = EMPTY_ELEMENTDATA;
} else {
//传入容量小于0,抛出不合法容量的异常
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
public ArrayList() {
//使用默认空实例进行初始化赋值
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//传入集合进行初始化,会将该集合转化为array,再逐元素进行拷贝,
//拷贝元素用到了Arrays类的copyOf静态方法
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
this.elementData = EMPTY_ELEMENTDATA;
}
}
常用方法:
trimToSize方法:
public void trimToSize() {
modCount++;//操作数加1
if (size < elementData.length) {
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
//判断,为0则赋值为空数组,否则根据size重新创建数组并拷贝元素,以达到重新裁剪容量的目的
}
}
由于扩容机制的存在,使得elementData中我们存入的元素个数size不一定等于其真实长度,所以提供了这个方法将elementData修剪,去掉多余的长度
看一下Arrays.copyOf方法:
@SuppressWarnings("unchecked")
public static <T> T[] copyOf(T[] original, int newLength) {
return (T[]) copyOf(original, newLength, original.getClass());
}
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;
}
其原理就是生成一个新的数组,长度取传入的长度,然后将original数组的元素通过System.arraycopy方法拷贝进去,长度取original的长度与传入长度newLength中的较小值。
由于trimToSize中传入的newLength为size,elementData的长度也为size,所以此处会将一个生成的数组赋值给elementData,这个数组元素和elementData中的元素相同,长度也变回了size。
关于System.arraycopy:
public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
参数解释:
Object src : 原数组
int srcPos : 从元数据的起始位置开始
Object dest : 目标数组
int destPos : 目标数组的开始起始位置
int length : 要copy的数组的长度
扩容机制:
public void ensureCapacity(int minCapacity) {//确保数组容量
//若为使用无参构造进行赋值的elementData,即默认空数组,则最小扩容为0,否则为10
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
? 0
: DEFAULT_CAPACITY;
if (minCapacity > minExpand) {//minCapacity大于数组容量,则扩容
ensureExplicitCapacity(minCapacity);
}
}
//计算最小容量,只在ensureCapacityInternal中使用,若是通过无参方法赋值的elementData,
//则会比较扩容量与传入的minCapacity,取较大值
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
//根据上面方法返回的数值大小,调用ensureExplicitCapacity方法,决定是否扩容
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
//最大长度,可能会造成OOM异常
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
//扩容方法
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
//右移一位等于除以二,这里可以看出ArrayList扩容机制是一点五倍扩容的
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
//最大容量
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
取得元素个数、判空、包含、get与set:
public int size() {
return size;
}
public boolean isEmpty() {
return size == 0;
}
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
这三个方法比较简单,不赘述。
get与set方法:
取下标:
//正向遍历,取第一个相同元素下标
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
//逆向遍历,取最后一个相同元素的下表
public int lastIndexOf(Object o) {
if (o == null) {
for (int i = size-1; i >= 0; i--)
if (elementData[i]==null)
return i;
} else {
for (int i = size-1; i >= 0; i--)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
从这里可以看出ArrayList是允许存储null的
clone方法:
public Object clone() {
try {
//首先调用Object的克隆方法
ArrayList<?> v = (ArrayList<?>) super.clone();
//将自身的elementData的所有元素拷贝到v里面,这里的元素还是同一引用,因此是浅克隆
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
添加方法:
//默认添加到尾部
public boolean add(E e) {
ensureCapacityInternal(size + 1);//确保数组长度,不够会加扩容
elementData[size++] = e;//将size的下一号元素赋值为e
return true;
}
//添加到指定位置
public void add(int index, E element) {
rangeCheckForAdd(index);//检查是否下标越界
ensureCapacityInternal(size + 1); //同上
//这是一个native方法
System.arraycopy(elementData, index, elementData, index + 1,
size - index);//将从index后面的数据都向后移一位
elementData[index] = element;//给index下标元素赋值为传入值element
size++;
}
//将制定集合的元素拷贝到elementData中
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray(); //toArray方法转化为数组
int numNew = a.length;
ensureCapacityInternal(size + numNew);
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
//将制定集合的元素拷贝到elementData从index下标开始的位置中
public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index);
//toArray方法转化为数组
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); //确保长度
int numMoved = size - index;
if (numMoved > 0)
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);//将index之后的所有数据往后移动numMoved位
//将数组a的所有元素拷贝到element的index开始的位置
System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;
return numNew != 0;
}
在添加元素的时候会调用ensureCapacityInternal方法,当我们采用无参构造生成ArrayList对象的时候,elementData成员会被赋值为DEFAULTCAPACITY_EMPTY_ELEMENTDATA实例。此时,当我们为存入任何元素时,calculateCapacity的计算结果值为10(因为初始的size为0,size+1与10比较,10更大,还是会返回10)。在添加元素的时候,ensureCapacityInternal就会通过调用ensureExplicitCapacity方法将数组扩容为10。这就能解释为什么ArrayList的成员信息中,我们可以看到DEFAULTCAPACITY_EMPTY_ELEMENTDATA实例为一个空数组,但是注释却说其是一个长度为10的空数组了。以后直到size大于等于10了,才会再次扩容,每次扩容为原来的1.5倍。
检查下标是否越界的方法rangeCheckForAdd:
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
toArray方法:
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
public <T> T[] toArray(T[] a) {
if (a.length < size)
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
无参的toArray可以将其自身转化为数组,带参的toArray可以将自身转化为指定类型的数组。
删除方法:
//删除指定下标的元素
public E remove(int index) {
rangeCheck(index);//检查下标
modCount++;//操作数加1
E oldValue = elementData(index);
int numMoved = size - index - 1;//要移动的元素个数
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);//从index+1开始将numMoved个元素向前移动一位
elementData[--size] = null;//原最后一个元素赋空
return oldValue;//返回删除的元素
}
//删除指定元素,删除成功返回false
public boolean remove(Object o) {
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;
}
//快速删除,不用检测下标
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;
}
//批量删除
private boolean batchRemove(Collection<?> c, boolean complement) {
final Object[] elementData = this.elementData;
int r = 0, w = 0;
boolean modified = false;
try {
for (; r < size; r++)
if (c.contains(elementData[r]) == complement)
elementData[w++] = elementData[r];
} finally {
if (r != size) {
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
if (w != size) {
// clear to let GC do its work
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
size = w;
modified = true;
}
}
return modified;
}
//删除指定范围的元素
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;
}
检查下标的方法rangeCheck:、
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
get与set方法:
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
清除所有元素:
public void clear() {
modCount++;
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
保留指定元素:
public boolean retainAll(Collection<?> c) {
Objects.requireNonNull(c);//检测是否为空
return batchRemove(c, true);
}
读写对象:(用于网络传输)
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException{
int expectedModCount = modCount;
s.defaultWriteObject();
s.writeInt(size);
for (int i=0; i<size; i++) {
s.writeObject(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
elementData = EMPTY_ELEMENTDATA;
s.defaultReadObject();
s.readInt(); // ignored
if (size > 0) {
int capacity = calculateCapacity(elementData, size);
SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity);
ensureCapacityInternal(size);
Object[] a = elementData;
for (int i=0; i<size; i++) {
a[i] = s.readObject();
}
}
}
迭代器:
public ListIterator<E> listIterator(int index) {
if (index < 0 || index > size)
throw new IndexOutOfBoundsException("Index: "+index);
return new ListItr(index);
}
public ListIterator<E> listIterator() {
return new ListItr(0);
}
public Iterator<E> iterator() {
return new Itr();
}
ArrayList有四个内部类:
private class Itr implements Iterator<E>
private class ListItr extends Itr implements ListIterator<E>
private class SubList extends AbstractList<E> implements RandomAccess
static final class ArrayListSpliterator<E> implements Spliterator<E>
说明:
内部迭代器Itr,ArrayList的iterator获取的迭代器就是这个内部类的对象,Itr对象是顺序迭代器对象。
内部迭代器ListItr,Itr的子类,对其进行了扩展。
内部类SubList,表示list某个区间的所有元素集合
内部迭代器ArrayListSpliterator,支持并发迭代。