public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
private static final int DEFAULT_CAPACITY = 10;
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
transient Object[] elementData;
private int size;
}
复制代码
首先能够看到ArrayList
继承了AbstractList
并实现了List
,也就是说ArrayList
是一个数组队列,拥有了基本的增删改查,遍历的操做。java
同时实现了数组
Cloneable
接口,便可以被clone
Serializable
接口,意味着能够被序列化RandomAccess
接口,RandomAccess
接口是一个空的接口,和Serializable
接口同样,也是做为一个标识,便可以快速访问,对于ArrayList
来讲,就是能够经过下标来访问元素。而LinkedList
就没有实现这*个接口。根据官方注解知道,DEFAULT_CAPACITY
是数组总空间大小,而size
是数组的当前的容量的大小。举个例子来讲,一个能够装1L水的杯子,那么DEFAULT_CAPACITY
就是1L,咱们如今往杯子里倒入了0.5L水,那么size
就是0.5L。固然在这,DEFAULT_CAPACITY
的默认值是10,当size
>10的时候,会进行扩容。安全
刚才咱们讲到ArrayList
内部其实就是用了一个Object[]
来进行维护数据,那既然咱们已经实现了Serializable
接口,那为撒还要用transient
来修饰elementData
呢?来看看序列化/反序列化的代码dom
private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException {
// Read in size, and any hidden stuff
s.defaultReadObject();
// Read in capacity
s.readInt(); // ignored
if (size > 0) {
// like clone(), allocate array based upon size not capacity
SharedSecrets.getJavaObjectInputStreamAccess().checkArray(s, Object[].class, size);
Object[] elements = new Object[size];
// Read in all elements in the proper order.
for (int i = 0; i < size; i++) {
elements[i] = s.readObject();
}
elementData = elements;
} else if (size == 0) {
elementData = EMPTY_ELEMENTDATA;
} else {
throw new java.io.InvalidObjectException("Invalid size: " + size);
}
}
private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException {
// Write out element count, and any hidden stuff
int expectedModCount = modCount;
s.defaultWriteObject();
// Write out size as capacity for behavioral compatibility with clone()
s.writeInt(size);
// Write out all elements in the proper order.
for (int i=0; i<size; i++) {
s.writeObject(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
复制代码
ArrayList
在序列化的时候会调用writeObject
方法,将数组的size
和elementData
写入ObjectOutputStream
,函数
在反序列化时调用readObject
,从ObjectInputStream
获取size
和elementData
,再恢复到elementData
.工具
这样就能够很好的节省空间和时间。由于elementData
整个的大小是CAPACITY
,通常状况下都会预留一些容量,咱们真正须要序列化/反序列化的只是当前存入的数据。post
public ArrayList() {
this.elementData = DEFAULTCAPACITY_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);
}
}
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 {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
复制代码
第一个空参构造函数,会默认将DEFAULTCAPACITY_EMPTY_ELEMENTDATA
的引用传入elementData
。在第一次添加元素的时候会扩容一个容量为10的数组。this
第二个指定初始化的 capacity
来建立 elementData
, 通常推荐使用这种方式来建立ArrayList
,减小扩容带来的内存开销。spa
第三个则是传入一个Collection
来建立elementData
线程
添加方法有三个重载方法,最终都会调用下面这个add
方法
private void add(E e, Object[] elementData, int s) {
if (s == elementData.length)
elementData = grow();
elementData[s] = e;
size = s + 1;
}
private Object[] grow() {
return grow(size + 1);
}
private Object[] grow(int minCapacity) {
return elementData = Arrays.copyOf(elementData,
newCapacity(minCapacity));
}
private int newCapacity(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); // 每次扩容1.5倍
//若是扩容后的capcity的大小等于或小于mincapacity,且若是是使用空参构造器初始化的,那么就返回Math.max(DEFAULT_CAPACITY, minCapacity),不然返回 minCapacity(if minCapacity > 0)
if (newCapacity - minCapacity <= 0) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) // 使用空参构造函数elementData=DEFAULTCAPACITY_EMPTY_ELEMENTDATA
return Math.max(DEFAULT_CAPACITY, minCapacity);
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return minCapacity;
}
return (newCapacity - MAX_ARRAY_SIZE <= 0)
? newCapacity
: hugeCapacity(minCapacity);
}
复制代码
在第一个方法中,e
是指当前add
的元素,s
指当前arrList
的size。能够发现,当size
的大小和elementData
的长度相等时,才会进行扩容,即调用grow
,从newCapacity
方法中能够发现,每次扩容会是当前size
(即 elementData.length
)的1.5倍。
public void add(int index, E element) {
rangeCheckForAdd(index);
modCount++;
final int s;
Object[] elementData;
if ((s = size) == (elementData = this.elementData).length)
elementData = grow();
System.arraycopy(elementData, index,
elementData, index + 1,
s - index);
elementData[index] = element;
size = s + 1;
}
复制代码
在指定下标条件元素的步骤
index
,若是index
小于0,或者大于size
,那么会抛出IndexOutOfBoundsException
异常modCount++
,后续会介绍用处arrayList
的size
和当前elementData
的长度是否相等,若是至关那么先进行扩容System.arraycopy
进行复制,把index
这个下标空出来public E remove(int index) {
Objects.checkIndex(index, size);
final Object[] es = elementData;
@SuppressWarnings("unchecked") E oldValue = (E) es[index];
fastRemove(es, index);
return oldValue;
}
private void fastRemove(Object[] es, int i) {
modCount++;
final int newSize;
if ((newSize = size - 1) > i)
System.arraycopy(es, i + 1, es, i, newSize - i);
es[size = newSize] = null;
}
复制代码
对于remove(int index)
源码就比较简单了,惟一须要注意的就是在fastRemove
中的判断,若是须要删除的是数组的最后一个元素,那么直接将最后一个元素设置为null
,不然进行arraycopy
,将index
的值覆盖掉。
前面咱们常常看到modCount++
的操做,那么为何要加这个呢?其实当每次对数组进行操做(修改)的时候,都会进行modCount++
,这样作是为了记录修改次数。
咱们知道 ArrayList
不是线程安全的,所以若是在使用迭代器的过程当中若是有其余线程修改(新增/删除)了arrayList
的数据(或当前线程在遍历的过程当中对数据进行修改),那么将抛出ConcurrentModificationException
,这就是所谓fail-fast策略。
在每次进行遍历的时候,会先将modCount
赋值给expectedCount
,在迭代过程当中,进行判断,若是它们不相等,则说明arrayList
的数据已经被修改,抛出异常。
Arrays.copyOf(T[], int length)
方法是 Arrays
工具类中用来进行任意类型数组赋值,并使数组具备指定长度的方法,ArrayList
中用这个方法来实现 elementData
数组的元素移动。但实际上 Arrays.copyOf
方法最终调用的是 System.arraycopy(U[], int srcPos, T[], desPos, int length)
方法,这个方法是一个native
方法
public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
复制代码