美文网首页
ArrayList add方法源码解读

ArrayList add方法源码解读

作者: 粽十三丶 | 来源:发表于2021-03-30 17:56 被阅读0次

    ArrayList<String> list = new ArrayList<>();

            list.add("a"); //调用list的add方法

    执行流程

    1.看代码

    /**

    * 将指定的元素追加到此列表的末尾。

    *

    * @param 要附加到此列表的元素

    * @return <tt>true</tt> (as specified by {@link Collection#add})

    */

    public boolean add(E e) {

    // 增加容器容量!!,size表示已经写入元素的数量

    ensureCapacityInternal(size + 1); 

    elementData[size++] = e;

    return true;

    }

    2.进入ensureCapacityInternal方法

    //size + 1:已经写入元素的数量+1(新增了一个元素)

    private void ensureCapacityInternal(int minCapacity) {

    //调用calculateCapacity方法,传入当前数组以及扩容后的容量

    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));

    }

    3.进入calculateCapacity方法计算需要扩容的大小

    private static int calculateCapacity(Object[] elementData, int minCapacity) {

    //如果当前数组等于默认大小为空的空数组,那么Math.max函数取最大值传入的两个参数为(默认初始容量10,1),所以初始扩容为10

    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {

    return Math.max(DEFAULT_CAPACITY, minCapacity);

    }

    //如果不等于,那么扩容的大小为数组容量+1

    return minCapacity;

    }

    4.调用ensureExplicitCapacity方法判断arrayList容器是否需要扩容

    private void ensureExplicitCapacity(int minCapacity) {

    //modcount对象来源于父类AbstractList,表示此数组在结构上被修改的次数,所以此处+1

    modCount++;

    // overflow-conscious code:该注解解释为,此方法考虑到容器可能出现溢出的情况,所以判断当前需要扩容的长度减去当前数组的长度大于0,那么需要扩容

    if (minCapacity - elementData.length > 0)

    //容器扩容

    grow(minCapacity);

    }

    5.调用grow方法开始扩容,入参:需要扩容的大小

    private void grow(int minCapacity) {

            // overflow-conscious code

    //数组扩容之前的大小

            int oldCapacity = elementData.length;

    //新的数组容量=老的数组长度的1.5倍。oldCapacity >> 1 相当于除以2

            int newCapacity = oldCapacity + (oldCapacity >> 1);

    //如果新的数组容量newCapacity小于传入的参数要求的最小容量minCapacity,那么新的数组容量以传入的容量参数为准

            if (newCapacity - minCapacity < 0){

    //否则按照原来容量的1.5倍扩容

                newCapacity = minCapacity;

    }

    //如果新的数组容量newCapacity大于数组能容纳的最大元素个数 MAX_ARRAY_SIZE 2^{31}-1-8,也就是int的最大值-9=3147483639

            if (newCapacity - MAX_ARRAY_SIZE > 0){

    //走入此方法,那么数组需要扩容的大小已经超过预设的容器最大容量了

    //那么此处会出现三种情况,1:如果预期扩容大小>int的最大值,那么会抛出内存溢出 2:如果预期扩容大小介于容器最大值~int的最大值之间,那么此时会扩容最后一次,扩容后的大小为int的最大值 3:小于容器的最大值,则按照容器的最大值扩容

    //具体方法实现看下面hugeCapacity方法的实现

                newCapacity = hugeCapacity(minCapacity);

    }

            // minCapacity is usually close to size, so this is a win:

            elementData = Arrays.copyOf(elementData, newCapacity);

        }

    //传入上面方法中预期扩容的数组长度10

    private static int hugeCapacity(int minCapacity) {

    //如果预期扩容长度为负数。则抛出堆内存溢出异常OutOfMemoryError

    //(ps:因为int的最大值为2147483647,也就是0x7fffffff如果你对2147483647+1.输出的就是-2147483648。这个数是负数中最大的数,也就是int型可以表示的最小的负数。它的十六进制表示为:0x8fffffff,8的二进制形式最高位是符号位,是1,为负。)

    //所以此处判断小于0则抛出内存溢出

            if (minCapacity < 0){

                throw new OutOfMemoryError();

    }

    //判断预期扩容长度和容器最大值的大小:大于则返回 int的最大值,否则返回数组长度预设的最大值

            return (minCapacity > MAX_ARRAY_SIZE) ?

                Integer.MAX_VALUE :

                MAX_ARRAY_SIZE;

        }

    6.调用Arrays.copyOf()实现扩容

    //original:需要扩容的数组对象

    //newLength: 需要扩容的长度

    //newType: original的class对象

    public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {

            @SuppressWarnings("unchecked")

    //创建新长度的数组

            T[] copy = ((Object)newType == (Object)Object[].class)

                ? (T[]) new Object[newLength]

                : (T[]) Array.newInstance(newType.getComponentType(), newLength);

    //此处System.arraycopy方法为native方法,已经追踪不到源码了,已经是操作堆内存了,具体大概为:将扩容后的数组索引位的值的引用指向了扩容前的数组对应的索引位

            System.arraycopy(original, 0, copy, 0,

                            Math.min(original.length, newLength));

    //返回新数组,扩容完成

            return copy;

        }

    7.元素添加完成

    public boolean add(E e) {

    //前面6步为上面ensureCapacityInternal方法的具体实现流程

            ensureCapacityInternal(size + 1);  // Increments modCount!!

    //多啰嗦一句,如果新增一个元素,那么size会加1,size表示数组元素的长度,所以size++在完成新元素的添加后,加1

    //此处元素add方法已经完成啦 ^.^

            elementData[size++] = e;

            return true;

        }

    相关文章

      网友评论

          本文标题:ArrayList add方法源码解读

          本文链接:https://www.haomeiwen.com/subject/eimghltx.html