从 ArrayList 源码看扩容原理

1. 类定义

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{}

2. 成员变量

private static final long serialVersionUID = 8683452581122892189L;

private static final int DEFAULT_CAPACITY = 10;  // 预备初始化大小

private static final Object[] EMPTY_ELEMENTDATA = {};  // 指定空数组

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};  // 默认空数组

transient Object[] elementData; // 存放元素的容器,非私有以简化嵌套类访问

private int size;  // 容器中实际元素个数。

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;  // 数组最大长度

3. 构造方法

// 无参构造,初始化为默认空数组
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

// 指定初始化大小
public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {  // 如果指定初始化大小为 0,就初始化为空数组。
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
    }
}

// 构造一个指定集合的集合
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;
    }
}

4. add 方法

  • 添加指定元素
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // 确保内部容量,传参为元素个数 + 1,如果需要扩容就扩容,不需要就什么都不做
    elementData[size++] = e;
    return true;
}
  • 确定容器内部容量
private void ensureCapacityInternal(int minCapacity) {
    // 如果 elementData 采用的是默认构造函数,就指定默认大小为 DEFAULT_CAPACITY=10
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }

    ensureExplicitCapacity(minCapacity);  // 确定明确的容量大小
}
  • 确定明确的容器大小
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;  // 修改次数,默认值是 0,这是父类 AbstractList 中的成员属性

    // 所需最小容量超过当前数组容量,就需要扩容
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}
  • 扩容
private void grow(int minCapacity) {
    int oldCapacity = elementData.length;  // 原数组容量
    int newCapacity = oldCapacity + (oldCapacity >> 1);  // 先扩容 1.5 倍,二进制补码,右移一位相当于除 2,所以 y = x + 0.5x = 1.5x

    // 扩容 1.5 倍后,比原数组的容量少,可能溢出了,导致二进制补码扩大 1.5 倍时,最高位变为 1,变成负数。
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;

    // 如果扩容 1.5 倍后 或 所需最小容量 超出预警的最大运行容量,需要对大容量进行进一步处理
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);  // 如果溢出,会抛出存储错误,没有溢出可能返回:MAX_ARRAY_SIZE 或 Integer.MAX_VALUE

    // 扩容基于数组复制
    elementData = Arrays.copyOf(elementData, newCapacity);
}
  • 进一步处理较大容量
private static int hugeCapacity(int minCapacity) {
    // 如果所需最小容量小于 0,说明溢出了。
    if (minCapacity < 0)
        throw new OutOfMemoryError();  // 抛出内容超出错误。
    // 没有溢出
    // 当所需最小容量大于 MAX_ARRAY_SIZE 时,返回 Integer.MAX_VALUE, 否则返回 MAX_ARRAY_SIZE
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
}

5. 总结

  • 用无参构造创建的 ArrayList 的初始化容量为 0,添加第一个元素时会重新得到一个初始化容量为 10 的数组。

  • 扩容,根据原数组的容量确定,扩容后的容量有下面四种情况:

    • 原数组容量的 1.5 倍;
    • 最小需求容量 minCapacity, minCapacity=原数组容量+1
    • MAX_ARRAY_SIZE,最大预警容量;
    • Integer.MAX_VALUE,最大允许容量;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值