ArrayList 扩容分析

ArrayList 初始容量

ArrayList 有多个不同的构造方法,不同的构造方法的初始容量是不同的。介绍之前先看下 ArrayList 都有哪些变量

	// 默认初始化容量=10
	private static final int DEFAULT_CAPACITY = 10;
	// 空数组,当初始化容量为0时返回该数组
	private static final Object[] EMPTY_ELEMENTDATA = {};
	// 空数组,用于跟 EMPTY_ELEMENTDATA 区分开来,当使用默认构造方法创建的时候返回该数组
	private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
	// 元素存放的数组
	transient Object[] elementData;
	// 元素个数(即list长度)
	private int size;
	// 记录数组被修改的次数
	protected transient int modCount = 0;
	// 数组最大长度
	private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8
	
  1. 无参构造方法,使用默认容量(默认容量为 10),并且设置 elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA
  2. 传入初始容量(initialCapacity),判断传入容量的值。值 > 0,则 new 一个长度为 initialCapacity 的 Object 数组;值 < 0,直接设置 elementData = EMPTY_ELEMENTDATA
  3. 传入一个 Collection,如果 Collection 的长度为 0,同样的,设置 elementData = EMPTY_ELEMENTDATA;否则,调用 CollectiontoArray 方法创建一个数组,并复制给 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];
	    } else if (initialCapacity == 0) {
	        this.elementData = EMPTY_ELEMENTDATA;
	    } else {
	        throw new IllegalArgumentException("Illegal Capacity: "+
	                                           initialCapacity);
	    }
	}
	
	/**
	 * Constructs an empty list with an initial capacity of ten.
	 */
	public ArrayList() {
	   this.elementData = DEFAULTCAPACITY_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
	 * @throws NullPointerException if the specified collection is null
	 */
	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;
	    }
	}
	

ArrayList 相关定义

ArrayList 有两个概念,一个是 capacity,它表示“容量”,表示的是 elementData 的长度。另一个是 size,表示的是数组中"存放元素的个数,这个很好理解,我们经常调用 ArrayListsize() 方法返回的就是这个 size

ArrayList 如何实现扩容的?

以无参构造方法为例,初始容量为 10,当调用 add(E e) 方法往 ArrayList 中插入数据的时候,才真正分配容量,即调用 Add(E e) 方法添加第一个元素时,数组扩容为 10

add(E e) 方法为入口,分析源码

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

每次调用 add(E e) 方法,都会调用 ensureCapacityInternal 方法,调用完 ensureCapacityInternal 方法后,将元素添加到 elementData 数组的尾部,进入到 ensureCapacityInternal 方法

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

	private void ensureExplicitCapacity(int minCapacity) {
	    modCount++;
	    // overflow-conscious code
	    if (minCapacity - elementData.length > 0)
	        grow(minCapacity);
	}
	

add 第一个元素的时候,minCapacity 为 1 ,因为是添加第一个元素,此时 elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA 结果为true,通过 Math.max 方法计算出 minCapacity 为 10

接下来看 ensureExplicitCapacity 方法,方法判断 minCapacity 是否大于数组长度,大于的话,表示数组需要扩容,不扩容的话直接往数组添加元素会导致数组越界异常,掉调用 grow() 方法进行数组的扩容

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

默认情况下,新建容量(newCapacity)会是原来容量(oldCapacity)的 1.5 倍(这里使用了 >> 位运算符提高效率),一般情况下,如果扩容 1.5 倍后就大于最小容量(minCapacity),则使用这个 1.5 倍的容量,如果小于最小容量,就返回最小容量的值

当新建容量(newCapacity)大于最大数组长度(MAX_ARRAY_SIZE)的时候,调用 hugeCapacity 方法

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

该方法很简单,当 minCapacity < 0 的时候,抛出内存溢出异常,否则返回最大数组长度

最后,调用 Arrays.copyOf 方法新建一个原数组的拷贝,并修改原数组指向新创建的数组。原数组会被垃圾回收器回收

扩容总结

一般情况下,ArrayList 在调用 add 方法的时候,如果 size 长度不够,则会进行扩容操作,将数组长度扩容到原来的 1.5 倍。如果频繁对 ArrayList 新增元素,则会频繁执行扩容操作影响性能

为避免频繁扩容造成性能下降。当知道将要存入数组的元素数量的时候,可以调用 ArrayList(int initialCapacity) 构造方法显示指定 ArrayList 的初始容量;或者可以通过手动调用 ensureExplicitCapacity(int minCapacity) 方法指定扩容长度,降低默认扩容频率

ArrayList 有缩容吗?

ArrayList 没有缩容。无论是 remove 方法还是 clear 方法,它们都不会改变现有数组 elementData 的长度。但是它们都会把相应位置的元素设置为 null,以便垃圾收集器回收掉不使用的元素,节省内存。

公众号

觉得对你有帮助可以关注我的公众号,一起学习,一起进步
微信搜索"簧笑宇"公众号

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值