面试题1:说一下ArrayList的扩容机制?add方法底层是什么?
答:
1.创建ArraytList
在创建ArrayList集合时,首先调用ArrayList无参构造方法
List list=new ArrayList();
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
此时,数组长度为0(默认容量),是个空数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
- 添加集合元素
List.add(“aa”);
当用add()方法添加元素时,首先执行ensureCapacityInternal(size+1)(确定内部容量)方法,用于校验原数组elementDate(元数据)的长度,为elementDate数组长度初始化做准备
注:这里源码用到的是ArrayList中的add方法,而不是list接口里的add方法
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
3.确定内部容量
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 static final int DEFAULT_CAPACITY = 10;
注:ensureExplicitCapacity(确认明确的容量)
此时调用其中的calculateCapacity(计算容量)方法,分为两种情况:
1)elementDate初始化时是空的数组,则minCapacity=size+1=1,那么则会将minCapacity=10,即拟初始化一个数组长度为10(并不是真的初始化,后期要做判断)
2)elementDate初始化时不是空的数组,则minCapacity=size+1,即拟初始化一个数组长度为size+1
-
扩容
private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); }
注:其中modCount译为该字段表示list结构上被修改的次数。结构上的修改指的是那些改变了list的长度大小或者使得遍历过程中产生不正确的结果的其它方式。
然后调用ensureExplicitCapacity方法,用于检查是否需要扩容,若所需长度超过了数组的长度,则需要扩容:
1)新建的空数组判断:minCapacity=10,elementData.length=0;则需要扩容
2)已存在的数组判断:minCapacity=size+1,elementData.length=size;则需要扩容
然后调用grow方法
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); }
扩容长度:原数组长度二进制码向右移一位
此时分为三种情况:
1)对于初始的空数组,则在此真正初始化elementData的长度为10,最后复制一个新数组,长度为10,数组地址发生改变;
2)已存在的数组按1.5倍扩容,没有超过最大容量的情况下,将数组扩容1.5倍,最后复制一个新数组,长度为原来的1.5倍,数组地址发生改变;
3)已存在的数组按1.5倍扩容,超过了最大容量的情况,将执行hugeCapacity(minCapacity)
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
注:Integer.MAX_VALUE=2147483647,则MAX_ARRAY_SIZE=2147483639;
private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
此时minCapacity=size+1;
此时分为两种情况:
-
如果mInCapacity<0 则抛出堆溢出异常
-
如果minCapacity>MAX_ARRAY_SIZE,则返回Integer.MAX_VALUE;反之将MAX_ARRAY_SIZE返回,这样的判断实际上是为了保证增加的一个元素可以添加到集合中去,但是集合的最大限制不能超过int的最大容量
最后复制一个新数组,长度为此函数的返回值,数组地址发生改变
-