ArrayList底层扩容机制

先来看下没有指定参数的List集合:

// 使用无参构造创建List集合
List list = new ArrayList();
// 添加数据
for (int i = 1; i <= 10; i++) {
    list.add(i);
}
// 添加数据
for (int i = 11; i <= 15; i++) {
    list.add(i);
}

话不多说直接上源码:

1、初始化集合

/**
 * 初始化一个容量为0的列表
 */
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

2、添加数据:涉及一个自动装箱

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

3、执行list.add方法:

1.这里进行判断容量是否够不够。

2.然后再进行赋值操作

/**
 * Appends the specified element to the end of this list.
 *
 * @param e element to be appended to this list
 * @return <tt>true</tt> (as specified by {@link Collection#add})
 */
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}

3.执行ensureCapacityInternal方法

  我们在for循环中添加的i就是minCapacity(1)跟DEFAULT_CAPACITY(常量为10),,进行比较,发现DEFAULT_CAPACITY大,就将这个10赋值给list集合无参构造器。也就是初始化容量为10

private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}

4、调用grow完成底层的扩容

modCount++:记录集合被修改的次数,防止有其他线程进来修改

minCapacity(10)-elementData.length(0) > 0;  实际上需要的最小容量 - Object数组初始化容量 > 0,elementData大小不够,才会调用底层的grow方法执行扩容。

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

5、grow扩容方法

第一次进来:

  elementData.length为0,oldCapacity + (oldCapacity >> 1)是等于0的;           那么第4行代码不会执行,也就是第一次不会执行扩容

第5行的if执行:

  if (newCapacity - minCapacity < 0)   第一次进来这个if语句会被执行,0 - 10 是小于0的,那么就将minCapacity(10)赋值给newCapacity,即newCapacity为10.

第7行的if不会执行:

  因为10-int类型最大值,不大于0

第10行的数组拷贝执行:

  就是说将elementData(0),扩容为newCapacity(10)大小,没有数据,那么就是数组中有10个空数据     

这里使用Arrays.copyOf()就是为了保留原有数组的数据的情况下,然后增加新的数组(null)

现在elementData为10个大小了,第二次及其以后都是按照1.5(oldCapacity + (oldCapacity >> 1)进行扩容了。)

 1 private void grow(int minCapacity) {
 2     // overflow-conscious code
 3     int oldCapacity = elementData.length;
 4     int newCapacity = oldCapacity + (oldCapacity >> 1);
 5     if (newCapacity - minCapacity < 0)
 6         newCapacity = minCapacity;
 7     if (newCapacity - MAX_ARRAY_SIZE > 0)
 8         newCapacity = hugeCapacity(minCapacity);
 9     // minCapacity is usually close to size, so this is a win:
10     elementData = Arrays.copyOf(elementData, newCapacity);
11 }

6、放入第一个数据

回到第二步的:elementData[size++] = e;

  elementData数组为10,这时候size默认是为0的,就将数据放到数组的第一个位置上面:elementData[0] = 1;

这里注意的是,第一次扩容完毕数组大小为10,只有这10个数组容量没有了才会继续执行扩容,就说明在第一次扩容后可以添加for循环中1-10这十个数据~

 7、第二次扩容:数组容量不够存放1-10个数据,执行扩容

执行到11-15数据的添加位置上:继续回到第五步的grow继续执行扩容

 

扩容后的elementData数组:

end:第三次扩容不显示库容后的容量问题

很多小伙伴在debug的时候,发现1-10正常显示,11-15正常显示(上图),但是执行到第三次扩容的时候(10+15/2 = 22)时候并没有看到null。

这是因为idea的原因,只需要在设置中调试一下:

 

对比一下没设置跟设置后的:

posted @ 2021-06-17 00:31  Java小白的搬砖路  阅读(193)  评论(0编辑  收藏  举报