知其然,知其所以然——ArrayList.add()详解

学数学的呢,有一个通病,好听点呢,就是想搞懂一切东西,当然,我们一般称其为:钻牛角尖,今天,我就想钻一钻ArrayList这一集合类的牛角尖

ArrayList

概述 ArrayList就是一个存放数据的集合类,可以看到API中的解释为:List接口的可调整大小的阵列实现。我们经常也能在LeetCode下刷到关于ArrayList的题目,接下来,我们就来看看这个类的真面目吧
类间关系 ArrayList类继承了AbstractList类,AbstractList又继承于AbstractCollection类,且实现了List接口,AbstractCollection类实现了Collection接口,图像形式如下:
ArrayList族谱
特点:由于是List接口的子类,有List的一切特点:有序,不唯一,且由于其本身的数组结构,令它有了另一个特点:查找快,增删慢

ArrayList用于添加元素方法有add和addAll,我们主要关注add(元素)方法

add(元素)

首先,先用一个例子来说明一下该方法的作用

package demo1;
import java.util.ArrayList;
public class Demo1 {
    public static void main(String[] args) {
        ArrayList<Integer> arr = new ArrayList<>();
        System.out.println("添加元素前:"+arr);
        arr.add(10);
        arr.add(50);
        arr.add(30);
        System.out.println("添加元素后:"+arr);

    }
}

结果为:
插入元素add()方法

从控制台上可以看出add()方法使ArrayList添加了元素,那么添加的原理是什么呢,我们直接上源码:
add()方法:

	public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

其中element[]数组便是ArrayList元素的储存位置,size 则代表数组有效长度,即元素个数。该方法首先用函数ensureCapacityInternal(size + 1)对数组进行操作,然后将元素存入数组最后位置。因为对ensureCapacityInternal(size + 1)函数的不了解,进入其源码:

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

    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

这一段代码的逻辑如下:当上面将size + 1 传入ensureCapacityInternal()函数时,将size + 1 作为形参minCapacity,也就是最小的所需容量。
用calculateCapacity函数判断原数组是否为空,为空的话,calculateCapacity函数将会返回默认容量和size+1 中的最大值,将返回值作为最小所需容量,加入ensureExplicitCapacity函数的参数,可以看到ensureExplicitCapacity函数有两步:
第一步是将modCount++,modCount属性是AbstractList中的一个整型变量,代表对集合的操作次数,在迭代器中有所作用,是一个控制线程安全的变量

第二步则是判断最小容量是否大于数组长度,大于则用grow()函数进行扩容操作。

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

    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

grow()函数主要就是将旧的数组长度进行1.5倍扩容成为新数组长度,这里用了一个非常巧妙的位运算增加了运算的效率,数组长度增大之后若仍小于默认值则将默认值赋值给数组长度,若大于最大的数组长度MAX_ARRAY_SIZE(Int最大值-8),则进入hugeCapacity函数,该函数也是一个判断语句,若最小所需长度为负数(超过int的最大值)则抛出内存溢出,否则若大于MAX_ARRAY_SIZE,则返回整型最大值,若更小,则返回
MAX_ARRAY_SIZE。
长度确定后,将原数组用Arrays.copyOf()函数复制到新数组中
ok,add()方法讲述完毕,主要便是数组扩容问题:
动态扩容

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值