在研究ArrayList扩容过程之前,先看下ArrayList的几个参数和构造函数:
// 默认的list的长度10 private static final int DEFAULT_CAPACITY = 10; // 空实例的共享空数组实例(有参构造函数使用),相比jdk1.7,这里是1.8版本优化,减少空数组产生 private static final Object[] EMPTY_ELEMENTDATA = {}; // 空实例的非共享数组(只有无参构造函数使用) private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; // 实际存储数据的对象 transient Object[] elementData; // 数组长度 private int size;
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); } }
1、初始长度大于0时,直接初始化一个当前长度的数组;
2、初始长度为0时,初始化一个空数组;
3、初始长度小于0时,抛出异常;
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; } }
传参为一个集合对象,直接将传参对象转换为数组,并把引用赋值给elementData数组。
1、数组对象长度大于0时,根据传参对象构造一个数组;
2、如果传参对象集合对象长度为0,则直接把空对象EMPTY_ELEMENTDATA赋值给elementData。
重点分析add(E e)函数(扩容就发生在新增接口哦中)
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); }
该方法将原数组长度加一作为扩容后的最小长度
如果是第一次向list中添加元素,则最小会把数组扩容为默认值10,(原来数组长度小于10),
通过Math.max(DEFAULT_CAPACITY, minCapacity)取DEFAULT_CAPACITY和minCapacity的最大值。
private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); }
这段代码特意加上了注释,其实下面那步是为了防止溢出的处理。
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); }
这段主要是为了防止数组长度超过数组默认的长度
Integer.MAX_VALUE - 8;
这里还有个有意思的地方:
@Native public static final int MAX_VALUE = 0x7fffffff;
默认的Integer的最大长度为2的31次-1,但是java限制的最大长度还要再减去8,其中在最长数组变量上有这么一句注释:
Some VMs reserve some header words in an array.
大概意思是某些VMs在数组前面会有标记长度的标记,所以减去的8为就是存这些东西了。
所以java(1.8)的意思大概是:我们规定数组的长度就是这么长啊,如果你一定要超过最长我也让你设,你自己心里有数就行(别说我非要扣掉你一些啊)。
最后就是把旧的数组里的元素赋值到新的元素,整个扩容过程结束。