Java集合精选常见面试题( 四 )

细心的同学一定会发现 :以无参数构造方法创建 ArrayList 时,实际上初始化赋值的是一个空数组 。当真正对数组进行添加元素操作时,才真正分配容量 。即向数组中添加第一个元素时,数组容量扩为 10 。下面在我们分析 ArrayList 扩容时会讲到这一点内容!

补充:JDK6 new 无参构造的 ArrayList 对象时,直接创建了长度是 10 的 Object[] 数组 elementData
add()
/*** 将指定的元素追加到此列表的末尾 。*/public boolean add(E e) {//添加元素之前,先调用ensureCapacityInternal方法ensureCapacityInternal(size + 1);// Increments modCount!!//这里看到ArrayList添加元素的实质就相当于为数组赋值elementData[size++] = e;return true;}ensureCapacityInternal()
(JDK7)可以看到 add 方法 首先调用了ensureCapacityInternal(size + 1)
//得到最小扩容量private void ensureCapacityInternal(int minCapacity) {if (elementData =https://www.huyubaike.com/biancheng/= DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {// 获取默认的容量和传入参数的较大值minCapacity = Math.max(DEFAULT_CAPACITY,minCapacity);}ensureExplicitCapacity(minCapacity);}如果调用 ensureCapacityInternal() 方法就一定会进入(执行)这个方法,下面我们来研究一下这个方法的源码!
//判断是否需要扩容private void ensureExplicitCapacity(int minCapacity) {modCount++;// overflow-conscious codeif (minCapacity - elementData.length > 0)//调用grow方法进行扩容,调用此方法代表已经开始扩容了grow(minCapacity);}仔细分析一下:
  • 当我们要 add 进第 1 个元素到 ArrayList 时,elementData.length 为 0 (因为还是一个空的 list),然后执行了 ensureCapacityInternal() 方法 ,所以 minCapacity 此时为 10 。此时,minCapacity - elementData.length > 0成立,所以会进入 grow(minCapacity) 方法 。
  • 当 add 第 2 个元素时,minCapacity 为 2,此时 e lementData.length(容量)在添加第一个元素后扩容成 10 了 。此时,minCapacity - elementData.length > 0 不成立,所以不会进入 (执行)grow(minCapacity) 方法 。
  • 添加第 3、4···到第 10 个元素时,依然不会执行 grow 方法,数组容量都为 10 。
直到添加第 11 个元素,minCapacity(为 11)比 elementData.length(为 10)要大 。进入 grow 方法进行扩容 。
grow()
/*** 要分配的最大数组大小*/private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;/*** ArrayList扩容的核心方法 。*/private void grow(int minCapacity) {// oldCapacity为旧容量,newCapacity为新容量int oldCapacity = elementData.length;//将oldCapacity 右移一位,其效果相当于oldCapacity /2,//我们知道位运算的速度远远快于整除运算,整句运算式的结果就是将新容量更新为旧容量的1.5倍,int newCapacity = oldCapacity + (oldCapacity >> 1);//然后检查新容量是否大于最小需要容量,若还是小于最小需要容量,那么就把最小需要容量当作数组的新容量,if (newCapacity - minCapacity < 0)newCapacity = minCapacity;// 如果新容量大于 MAX_ARRAY_SIZE,进入(执行) `hugeCapacity()` 方法来比较 minCapacity 和 MAX_ARRAY_SIZE,//如果minCapacity大于最大容量,则新容量则为`Integer.MAX_VALUE`,否则,新容量大小则为 MAX_ARRAY_SIZE 即为 `Integer.MAX_VALUE - 8` 。if (newCapacity - MAX_ARRAY_SIZE > 0)newCapacity = hugeCapacity(minCapacity);// minCapacity is usually close to size,so this is a win:elementData = https://www.huyubaike.com/biancheng/Arrays.copyOf(elementData,newCapacity);}int newCapacity = oldCapacity + (oldCapacity >> 1),所以 ArrayList 每次扩容之后容量都会变为原来的 1.5 倍左右(oldCapacity 为偶数就是 1.5 倍,否则是 1.5 倍左右)! 奇偶不同,比如 :10+10/2 = 15,33+33/2=49 。如果是奇数的话会丢掉小数.

经验总结扩展阅读