ArrayList的底层实现是数组,与普通的数组相比,ArrayList能实现容量的动态增长。在ArrayList中最重要的属性是:
elementData和size。elementData是Object[]的数组,size是数组中实际元素的大小。
transient Object[] elementData;
private int size;
(一)序列化
可以看到elementData被transient修饰了。transient用来说明elementData并不参与序列化的过程,而ArrayList是实现Serializable接口的,具体的序列化的过程在writeObject()和readObject()方法中,序列化的内容只取实际元素的元素(即size实际大小的元素)以减少io的损耗。以writeObject()为例:
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 behavioural 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();
}
}
(二)扩容
(1)什么时候扩容?
以向ArrayList中添加元素的add()方法为例:
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
可以看到当minCapacity大于elementData的容量时,便开始扩容,而minCapacity是由size+1和DEFAULT_CAPACITY(默认10)的大者决定的。
(2)扩多大的容?
先看下扩容的代码:
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity; //(tag 1)
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
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;
}
在上面的代码中,在tag 1之前newCapacity大小获取的思路是取minCapacity和oldCapacity扩大1.5倍(oldCapacity >> 1位移运算,计算的结果是oldCapacity的一半)的较大者。之后当newCapacity大于 MAX_ARRAY_SIZE时调用hugeCapacity()计算newCapacity新的值。其中MAX_ARRAY_SIZE=Integer.MAX_VALUE - 8。在官方的说明中,减8是因为一些虚拟机保留头信息,减8可以减少出错的概率。在hugeCapacity中可以看到 return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE :MAX_ARRAY_SIZE;,因此数组扩容后最大值依然为Integer.MAX_VALUE。