public class ArrayList<E> extends AbstractList<E> implements Cloneable, Serializable, RandomAccess
/**
* The minimum amount by which the capacity of an ArrayList will increase.
* This tuning parameter controls a time-space tradeoff. This value (12)
* gives empirically good results and is arguably consistent with the
* RI's specified default initial capacity of 10: instead of 10, we start
* with 0 (sans allocation) and jump to 12.<span style="white-space:pre"> </span>//这里最小不再是10
*/
private static final int MIN_CAPACITY_INCREMENT = 12;
/**
* The number of elements in this list.
*/
int size;
/**
* The elements in this list, followed by nulls.
*/
transient Object[] array;
成员变量也就是size和一个array数组。
上面这个对象数组就是其存储元素的数据结构,前面有一个java关键字transient,这个关键字是去序列化的意思,即,在这个类序列化后保存到磁盘或者输出到输出流的时候,这个对象数组是不被保存或者输出的。
这里又有疑问了,这个数组不是存储我们保存的数据的吗?为什么要去序列化呢?那么如果去掉序列化之后,我们保存的元素从哪里来呢?
这就跟这个ArrayList的特性有关,我们知道ArrayList的容量,也就是这个数组的容量,一般都是预留一些容量,等到容量不够时再拓展,那么就会出现容量还有冗余的情况,如果这时候进行序列化,整个数组都会被序列化,连后面没有意义空元素的也被序列化。这些是不应该被存储的。所以java的设计者,就为这个类提供了一个writeObject方法,在实现了Serializable接口的类,如果这个类提供了writeObject方法,那么在进行序列化的时候就会通过writeObject方法进行序列化,所以ArrayList的writeObject方法就会显式的为每个实际的数组元素进行序列化,只序列化有用的元素。
private void writeObject(ObjectOutputStream stream) throws IOException {
stream.defaultWriteObject(); //这里jdk1.8不太一样 1.8中首先有 <span class="keyword" style="font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; line-height: 1.5em; white-space: pre-wrap; text-indent: 1em; font-weight: bold;">int</span><span style="font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; line-height: 1.5em; white-space: pre-wrap; text-indent: 1em; background-color: rgb(246, 246, 246);"> expectedModCount = modCount;</span>
stream.writeInt(array.length);
for (int i = 0; i < size; i++) {
stream.writeObject(array[i]);
} //1.8最后还有一个判断expectedModCount==modCount 不等于抛出异常
}
好了,回归ArrayList的构造函数
public ArrayList(int capacity) {
if (capacity < 0) {
throw new IllegalArgumentException("capacity < 0: " + capacity);
}
array = (capacity == 0 ? EmptyArray.OBJECT : new Object[capacity]);
}
/**
* Constructs a new {@code ArrayList} instance with zero initial capacity.
*/
public ArrayList() {
array = EmptyArray.OBJECT;
}
这里空参时,并没有初始化容器的大小,而是在add()方法时
public boolean add(E object) {
Object[] a = array;
int s = size;
if (s == a.length) {
Object[] newArray = new Object[s +
(s < (MIN_CAPACITY_INCREMENT / 2) ?
MIN_CAPACITY_INCREMENT : s >> 1)]; //如果s小于6,新长度则为s+12,否则为s*3
System.arraycopy(a, 0, newArray, 0, s);
array = a = newArray;
}
a[s] = object;
size = s + 1;
modCount++;
return true;
}
下面这个是扩容的方法,在addAll()和add(int index,E e)时被调用,动态设置新的容量大小
private static int newCapacity(int currentCapacity) {
int increment = (currentCapacity < (MIN_CAPACITY_INCREMENT / 2) ?
MIN_CAPACITY_INCREMENT : currentCapacity >> 1);
return currentCapacity + increment;
}
仍然是保证扩容两倍倍,即大概是currentCapacity*3;下面这个方法是用户自己设置一个合适的最小容量,以便保证添加时的效率问题
/**
* Ensures that after this operation the {@code ArrayList} can hold the
* specified number of elements without further growing.
*
* @param minimumCapacity
* the minimum capacity asked for.
*/
public void ensureCapacity(int minimumCapacity) {
Object[] a = array;
if (a.length < minimumCapacity) {
Object[] newArray = new Object[minimumCapacity];
System.arraycopy(a, 0, newArray, 0, size);
array = newArray;
modCount++;
}
}
再看这个remove方法
public boolean remove(Object object) {
Object[] a = array;
int s = size;
if (object != null) {
for (int i = 0; i < s; i++) {
if (object.equals(a[i])) {
System.arraycopy(a, i + 1, a, i, --s - i);
a[s] = null; // Prevent memory leak
size = s;
modCount++;
return true;
}
}
} else {
for (int i = 0; i < s; i++) {
if (a[i] == null) {
System.arraycopy(a, i + 1, a, i, --s - i);
a[s] = null; // Prevent memory leak
size = s;
modCount++;
return true;
}
}
}
return false;
}
可以看出remove和add都有System.arraycopy的复制数组操作,故对于频繁的插入和删除,性能会较差。
其余的方法比如contains(),get(int index),就是遍历找到对应的元素,代码如下
public E get(int index) {
if (index >= size) {
throwIndexOutOfBoundsException(index, size);
}
return (E) array[index];
}
public boolean contains(Object object) {
Object[] a = array;
int s = size;
if (object != null) {
for (int i = 0; i < s; i++) {
if (object.equals(a[i])) {
return true;
}
}
} else {
for (int i = 0; i < s; i++) {
if (a[i] == null) {
return true;
}
}
}
return false;
}
最后说一下这个modCount
我们先说说 容器的“快速失败”机制,它是Java集合中的一种错误检测机制,当多个线程对集合进行结构上的改变的操作时,有可能会产生fail-fast机制。它是一种迭代器检测bug的机制,比如:我们有线程A和线程B,线程A通过Iterator访问集合中的元素,某个时刻,线程B修改了集合中的结构,那么程序就会抛出 ConcurrentModificationException 。在ArrayList中的迭代器中有:
/** The expected modCount value */
private int expectedModCount = modCount;
在方法中next()和remove()有这段代码
if (ourList.modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
arraylist中每次对数组结构改变一次就modCount+1,因此在迭代过程中,发现modCount != expectedModCount就会抛出异常,保证迭代过程中,arraylist中的数组没有发生改变。