• ArrayList扩容机制


    ArrayList源码:

    首先,新增一个元素时,会调用ensureCapacityInternal函数,传入当前需要的大小,确定是否需要扩容。

    1. /**
    2. * Appends the specified element to the end of this list.
    3. *
    4. * @param e element to be appended to this list
    5. * @return true (as specified by {@link Collection#add})
    6. */
    7. public boolean add(E e) {
    8. ensureCapacityInternal(size + 1); // Increments modCount!!
    9. elementData[size++] = e;
    10. return true;
    11. }

    size是成员变量,默认为0

    1. /**
    2. * The size of the ArrayList (the number of elements it contains).
    3. *
    4. * @serial
    5. */
    6. private int size;

    然后调用calculateCapacity确定当前需要的最小容量,调用ensureExplicitCapacity进行扩容

    1. private void ensureCapacityInternal(int minCapacity) {
    2. ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    3. }
    1. private static int calculateCapacity(Object[] elementData, int minCapacity) {
    2. // 如果还没有扩容过,获取DEFAULT_CAPACITY(默认值10)与minCapacity的最大值
    3. if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
    4. return Math.max(DEFAULT_CAPACITY, minCapacity);
    5. }
    6. return minCapacity;
    7. }

    modCount为AbstractList的成员变量,为扩容的次数

    1.当我们要 add 进第 1 个元素到 ArrayList 时,elementData.length 为 0 (因为还是一个空的 list),因为执行了 ensureCapacityInternal() 方法 ,所以 minCapacity 此时为 10。此时,minCapacity - elementData.length > 0 成立,所以会进入 grow(minCapacity) 方法。

    2.当 add 第 2 个元素时,已经扩容过了,calculateCapacity直接返回minCapacity ,也就是 2。

    此时 elementData.length(容量)在添加第一个元素后扩容成 10 了。此时,minCapacity - elementData.length > 0 不成立,所以不会进入 (执行)grow(minCapacity) 方法。

    1. private void ensureExplicitCapacity(int minCapacity) {
    2. modCount++;
    3. // overflow-conscious code
    4. // 如果第一次list的长度没有指定,minCapacity将为10
    5. // 初始化时,elementData.length默认为0,第一次添加元素就会扩容
    6. if (minCapacity - elementData.length > 0)
    7. grow(minCapacity);
    8. }

    进入核心函数grow():

    1. private void grow(int minCapacity) {
    2. // oldCapacity为旧容量,newCapacity为新容量
    3. int oldCapacity = elementData.length;
    4. //将oldCapacity 右移一位,其效果相当于oldCapacity /2,
    5. //我们知道位运算的速度远远快于整除运算,整句运算式的结果就是将新容量更新为旧容量的1.5倍,
    6. int newCapacity = oldCapacity + (oldCapacity >> 1);
    7. //然后检查新容量是否大于最小需要容量,若还是小于最小需要容量,那么就把最小需要容量当作数组的新容量,
    8. if (newCapacity - minCapacity < 0)
    9. newCapacity = minCapacity;
    10. // 如果新容量大于 MAX_ARRAY_SIZE,进入(执行) `hugeCapacity()` 方法来比较 minCapacity 和 MAX_ARRAY_SIZE,
    11. //如果minCapacity大于最大容量,则新容量则为`Integer.MAX_VALUE`,否则,新容量大小则为 MAX_ARRAY_SIZE 即为 `Integer.MAX_VALUE - 8`。
    12. if (newCapacity - MAX_ARRAY_SIZE > 0)
    13. newCapacity = hugeCapacity(minCapacity);
    14. // minCapacity is usually close to size, so this is a win:
    15. elementData = Arrays.copyOf(elementData, newCapacity);
    16. }

    注意,如果容量为奇数,右移一位会把小数丢掉,11+11/2 = 21

    最后进入Arrays.copyOf()方法:

    1. public static T[] copyOf(U[] original, int newLength, Classextends T[]> newType) {
    2. @SuppressWarnings("unchecked")
    3. T[] copy = ((Object)newType == (Object)Object[].class)
    4. ? (T[]) new Object[newLength]
    5. : (T[]) Array.newInstance(newType.getComponentType(), newLength);
    6. System.arraycopy(original, 0, copy, 0,
    7. Math.min(original.length, newLength));
    8. return copy;
    9. }

    这里 主要是创建了一个我们需要的容量的数组copy,然后把原数组的值辅助到copy中,再返回

    进入System.arraycopy()方法:

    1. /**
    2. * 复制数组
    3. * @param src 源数组
    4. * @param srcPos 源数组中的起始位置
    5. * @param dest 目标数组
    6. * @param destPos 目标数组中的起始位置
    7. * @param length 要复制的数组元素的数量,所以传入的是Math.min(original.length, newLength)
    8. */
    9. public static native void arraycopy(Object src, int srcPos,
    10. Object dest, int destPos,
    11. int length);

  • 相关阅读:
    机器学习11—原型聚类之学习向量量化(LVQ)
    【GIT版本控制】--项目管理与工具
    6月2(信息差)
    深度学习训练营实现minist手写数字识别
    在学习DNS的过程中给我的启发
    亚太C题详细版思路修改版(精)
    上市公司信息透明度数据(1991-2019年)包含stata源代码和数据
    C# API POST与GET的调用
    ConsensusClusterPlus包进行聚类分析
    Mac如何远程连接Ubuntu主机(一)ssh连接|Mac通过ssh远程连接Ubuntu主机
  • 原文地址:https://blog.csdn.net/weixin_45974277/article/details/128116728