ArrayList 如何自动扩容

数组最大的优势是根据下标获取数据的时间复杂度为O(1)。

但是数组有一个缺点就是需要一段连续的内存空间。而且数组大小不可以改变。如果数组满了,还要继续存储,那只能扩容数组。

有人可能会说,Java中java.util.ArrayList底层就是数组实现的,从来没有管过数组大小的问题啊。

其实是java.util.ArrayList自己实现了自动扩容。

现在我们就来分析一下,java.util.ArrayList 是如何自动扩容的。

源码采用的是JDK1.8

我们一般初始化一个ArrayList的方法是用它的构造方法

ArrayList list = new ArrayList();

ArrayList有3个构造方法,如下:
在这里插入图片描述

第一个不带参数的构造函数

/**
  * Constructs an empty list with an initial capacity of ten.
  */
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

注释这里虽然说了用初始化容量10构建一个空集合,但是这里默认是一个空的数组。难道JDK源码注释错了?大概率不会错吧,我们继续看。

我们看下 DEFAULTCAPACITY_EMPTY_ELEMENTDATA

/**
  * Shared empty array instance used for default sized empty instances. We
  * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
  * first element is added.
  */
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

注释大概的意思是,DEFAULTCAPACITY_EMPTY_ELEMENTDATA 用来表示默认大小的空数组实例,跟 EMPTY_ELEMENTDATA 做了区分,为了知道第一次增加元素的时候扩容多少。

第二个构造方法

可以简单理解为,传入一个集合,放到数组中。

/**
     * Constructs a list containing the elements of the specified
     * collection, in the order they are returned by the collection's
     * iterator.
     *
     * @param c the collection whose elements are to be placed into this list 传入一个Collection的实现
     * @throws NullPointerException if the specified collection is null
     */
public ArrayList(Collection<? extends E> c) {
    // 转成数组
    Object[] a = c.toArray();
    // 数组长度大于0
    if ((size = a.length) != 0) {
        // 如果是ArrayList类型
        if (c.getClass() == ArrayList.class) {
            // 直接赋值给当前对象
            elementData = a;
        } else {
            // 创建一个size大小的数组,将元素拷贝进去
            elementData = Arrays.copyOf(a, size, Object[].class);
        }
    } 
    // 数组长度等于0,就实例化一个空数组,注意,这里不是默认长度的数组,而是一个长度为0的空数组
    else {
        // replace with empty array.
        elementData = EMPTY_ELEMENTDATA;
    }
}

第三个构造方法

这个方法就更好理解了,创建一个指定大小的空数组

/**
     * Constructs an empty list with the specified initial capacity.
     *
     * @param  initialCapacity  the initial capacity of the list
     * @throws IllegalArgumentException if the specified initial capacity
     *         is negative
     */
public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];
    } 
    // 这里要注意下,如果传入的是0,则创建的也是一个空的数组,不是默认长度的空数组
    else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}

add()方法

调用add()方法的时候,首先会判断数组是否还有容量能够存数据,如果数组满了,进行扩容

    /**
     * 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;
    }

    private void ensureCapacityInternal(int minCapacity) {
        // minCapacity 最小长度
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        // 如果数组是默认大小的空数组,获取 DEFAULT_CAPACITY 和 minCapacity 中较大的值作为扩容容量
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            // DEFAULT_CAPACITY = 10
            // 这里,用不带参数的构造方法构造出来的ArrayList,这里会初始化到10
            // 当然这里要说明一下,如果用不带参数的构造方法构造出来的ArrayList,第一次就调用了addAll()添加超过了10个元素,那么这里就会取minCapacity了
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

    private void ensureExplicitCapacity(int minCapacity) {
        // 这里你可以先简答的认为这个 modCount 就是数组扩容的次数
        modCount++;

        // overflow-conscious code
        // 如果需要的最小长度比当前数组容量大,需要扩容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

    /**
     * Increases the capacity to ensure that it can hold at least the
     * number of elements specified by the minimum capacity argument.
     * 这个就是数组扩容的方法
     *
     * @param minCapacity the desired minimum capacity
     */
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        // 新容量是旧容量的1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        // 如果扩容1.5倍还不够,那就直接赋值为需要的长度
        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);
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值