ArrayList扩容原理-基于1.8
ArrayList 中的属性字段
这个是一个默认的容量,针对无参构造
private static final int DEFAULT_CAPACITY = 10;
空的数组
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
arrayList存放元素的位置,因为elementData的实际长度和list的长度可能是不等的,为了在序列化的时候节约空间,所以用了transient,具体的可以看看writeObject readObject
transient Object[] elementData;
list中元素的数量(区分一下elementData的长度)
private int size;
构造方法
无参
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
这里只是对elementData进行了初始化,给赋了一个空数组。没有进行扩容操作。
有参 initialCapacity
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);
}
}
这里的参数是一个初始容量。
小于0会抛出异常,等于0效果和无参构造一样。
有参 Collection<? extends E> c
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 {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
这里参数是一个集合,集合大小为0和无参没区别,
集合大小不为0的时候,会给size赋值。然后会判断class并进行复制。
扩容
ArrayList的扩容都是发生在添加新元素的过程中。
我们以 **add(E e)**方法为例:
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
调用add方法后,会先进行一个最小容量(size+1)的计算,若elementData为空数组,那么计算结果为DEFAULT_CAPACITY,否则将返回Math.max(size+1,DEFAULT_CAPACITY).
后面会比较计算的 minCapacity 和 elementData.size()大小,判断是否需要进行扩容。
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
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);
}
grow才是真正扩容的过程。
扩容的过程就是 elementData.size >> 1。然后判断和Integer.MAX_VALUE的差值,看看是否溢出。
- 若是使用addAll,可能存在elementData.size >> 1后还是不满足最小容量的条件 *
扩展:System源码中的arraycopy()标识为native意味JDK的本地库,不可避免的会进行IO操作,如果频繁的对ArrayList进行扩容,毫不疑问会降低ArrayList的使用性能,因此当我们确定添加元素的个数的时候,我们可以事先知道并指定ArrayList的可存储元素的个数,这样当我们向ArrayList中加入元素的时候,就可以避免ArrayList的自动扩容,从而提高ArrayList的性能。