ArrayList扩容机制
3种构造方法
-
无参构造
// 前置参数 // ArrayList里缓存数据的数组 transient Object[] elementData; // 空数组,用来无参构造创建一个空的数组对象 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; public ArrayList() { this.element = DEFAULTCAPACITY_EMPTY_ELEMENTDATA } -
有参构造
-
参数为
Collection的子类// 前置参数 // 空数组对象 private static final Object[] EMPTY_ELEMENTDATA = {}; public ArrayList(Collection<? extends E> c) { Object[] a = c.toArray(); if((size = a.length) != 0) { if(a.getClass == ArrayList.class) { element = a; }else { elment = Arrays.copyOf(a, a.length, Object.class); } }else { elment = EMPTY_ELEMENTDATA; } } -
参数为
int// 前置参数 // 空数组对象 private static final Object[] EMPTY_ELEMENTDATA = {}; public ArrayList(int initialCapacity) { if(initialCapacity > 0) { this.element = new Object[initialCapacity]; }else if(initialCapacity == 0) { this.element = EMPTY_ELEMENTDATA; }else { throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity); } }
从上面的三种构造方法中可以了解到,创建一个
ArrayList对象其实就是创建了一个数组对象,都知道数组一旦创建长度都是固定的,那么ArrayList的扩容机制又是怎么扩容的呢?ArrayList扩容
首先先来看下
add(E e)方法// 前置参数 private int size; transient Object[] elementData; private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; private static final int DEFAULT_CAPACITY = 10; protected transient int modCount = 0; private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; public boolean add(E e) { ensureCapacityInternal(size + 1); elementData[size++] = e; return true; } private void ensureCapacityInternal(int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); } private static int calculateCapacity(Object[] elementData, int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; } private void ensureExplicitCapacity(int minCapacity) { modCount++; if (minCapacity - elementData.length > 0) grow(minCapacity); } private void grow(int minCapacity) { int oldCapacity = elementData.length; 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; }我们一个个方法来看:
ensureCapacityInternal这个方法很简单主要是为了确定初始化的数组长度是多少- 而
calculateCapacity则是如果ArrayList对象是通过空参构造创建的,那么数组的长度就是10,否则就是当前数组长度+1 ensureExplicitCapacity判断当前的数组容量elementData.length是否满足这次新增的数组长度minCapacity如果不满足则需要进行扩容;而modCount用来统计队列发生的变化次数,modCount作用grow首先将原来的数组长度进行1.5倍扩容,如果扩容后的长度(newCapacity)还是小于新增元素长度(minCapacity)则直接使用新增后的数组长度,然后判断扩容后的长度(newCapacity)是否大于最大的数组长度,如果大于则需要调用hugeCapacity进行数组长度最大扩容hugeCapacity所谓数组长度最大化扩容就是判断扩容后的长度是否已经超过MAX_ARRAY_SIZE,如果已经超过则使用Integer最大值
总结: 判断
element是否是空数组,如果是空数组那么数组长度就是默认长度10,如果不是空数组,则判断当前数组长度是否小于新增数组长度,如果小于则调用grow()方法进行扩容,将原来的数组长度右移1(增加1.5左右),然后再判断一下扩容后的长度是否还小于新增数组长度(创建ArrayList空对象,数组长度为0,即使右移后也还是0),小于则将新增数组长度赋值给扩容数组长度,扩容后的数组长度和ArrayList的MAX_ARRAY_SIZE比较如果大于则扩容长度扩容至Integer最大值 -
其他思考
-
为什么会有两个空数组对象
EMPTY_ELEMENTDATA和DEFAULTCAPACITY_EMPTY_ELEMENTDATA答: jdk1.7时创建容量为0的
ArrayList对象时,会直接将EMPTY_ELEMENTDATA值赋值给element,如果一个应用中有多个ArrayList空对象就会有多个空数组,造成很多不必要的浪费.EMPTY_ELEMENTDATA是为了优化创建ArrayList空实例时产生不必要的空数组,使得所有ArrayList空实例都指向同一个空数组。DEFAULTCAPACITY_EMPTY_ELEMENTDATA是为了确保无参构成函数创建的实例在添加第一个元素时,最小的容量是默认大小10。