ArrayList源码分析,ArrayList如何扩容?

结论

  • ArrayList通过Object数组来存储元素,自动扩容机制让调用者可直接进行增删改查,而不用关心数组容量问题。
  • 扩容机制:
    • 容量不够时,进行1.5倍扩容
    • 特殊情况:通过无参构造创建ArrayList时,初始Object数组为空数组,容量为0,第一添加元素时,扩容到10

主要变量和构造方法

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable{

    //=========================几个变量===============================
    
    //默认容量
    private static final int DEFAULT_CAPACITY = 10;
    
    //内部Object数组,集合元素都保存在这个数组里
    transient Object[] elementData; 
    
    //当前集合内元素个数    
    private int size;

    //一个空数组
    //调用有参构造,手动指定容量为0时,elementData被赋值为它
    private static final Object[] EMPTY_ELEMENTDATA = {};

    //又一个空数组
    //调用无参构造,elementData被赋值为它
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    //序列化版本号
	private static final long serialVersionUID = 8683452581122892189L;
        
	//=========================主要构造方法===============================
    public ArrayList() {
        //调用无参构造,内部数组被赋值一个空数组(两个空数组的其中一个)
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
            
    public ArrayList(int initialCapacity) {
        //调用有参构造,指定一个初始容量
        if (initialCapacity > 0) {
            //容量大于0,内部数组被赋值为指定容量的Object数组。
        	this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            //若手指定容量为0,内部数组被赋值为一个空数组(两个空数组的其中一个)。
        	this.elementData = EMPTY_ELEMENTDATA;
        } else {
            //初始容量小于0,则抛出异常
        	throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
        }
    }
        
}

添加元素

public boolean add(E e) {
    ensureCapacityInternal(size + 1); 	//每次添加前,都要确保有“size + 1”个容量才行
                                    	//如:当前集合内已经有10个元素,要添加一个,最少得确保容量为11
    elementData[size++] = e;			//将要添加的元素放到数组中
    return true;						//返回true表示添加成功。
}

确保容量

//传入size + 1,表示需要“size + 1”这么大的容量
//进一步计算最小容量,再确定是否需要扩容
private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        //无参构造方法中执行过“elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA”
        //所以,第一次添加元素时,elementData没有被动过,会走到这
        
        return Math.max(DEFAULT_CAPACITY, minCapacity);
        //由此说明,无参构造,第一次添加元素时,最小容量设置为DEFAULT_CAPACITY,即10
    }
	
    //若不是无参构造,上面的if判断没用,这个方法也相当于没用
    return minCapacity;
}

//计算好了最小容量,看看是否需要扩容
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;		//父类AbstractList中继承过来的一个成员变量,表示表结构修改的次数
                	//主要是给迭代器使用的,迭代器遍历时,若发现该变量被修改,即结构被修改,则抛出异常
                	//避免在多线程并发时,其它线程对数组进行了修改,还继续遍历。


	//最小容量(所需容量)大于当前数组长度,说明容量不够
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);	//进行真正的扩容操作

	//否则,所需容量小于当前数组长度,说明容量够用,方法调用结束,不扩容。
}

//实际扩容
private void grow(int minCapacity) {
    
    int oldCapacity = elementData.length;					//获取旧容量
    int newCapacity = oldCapacity + (oldCapacity >> 1);		//计算新容量
                                                            // ">> 1" ,表示右移一位,表示除以2
                                                            //如:0000 0110(十进制6)
                                                            //右移一位高位补零 0000 0011(十进制3)
                                                            //所以新容量为旧容量的1.5倍

    //对新容量进行合法性判断
    if (newCapacity - minCapacity < 0)				//新容量比所需容量小???
        newCapacity = minCapacity;					//那怎么行,起码也要设置为所需容量才行
                                                	//其实主要发生在第一添加元素时,
                                                    //此时elementData.length 为 0
                                                    //所以,计算出来的newCapacity也为0
    
                                                    //当然还有一些其他情况,比如设置了初始容量为1
                                                    //计算出来的newCapacity为1,显然不合适
    
    if (newCapacity - MAX_ARRAY_SIZE > 0)			//新容量比规定的最大值(MAX_ARRAY_SIZE)要大
        newCapacity = hugeCapacity(minCapacity);	//通过hugeCapacity方法给一个合法的新容量
                                                    //旧容量太大时,比如超过规定最大容量的一半
                                                    //此时,1.5倍扩大后,肯定超过了规定的最大值
                                                    

    //到此,新容量没问题,调用Arrays.copyOf进行扩容
    //开辟一个大小为newCapacity的数组,将elementData中的数据挨个复制过去
    elementData = Arrays.copyOf(elementData, newCapacity);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ArrayListJava中常用的动态数组实现,它可以根据需要动态地增加或缩小数组的大小。当ArrayList中的元素数量超过了初始容量时,ArrayList会自动扩容,以便能够存储更多的元素。下面我们来看一下ArrayList扩容源码实现。 在ArrayList中,扩容是由ensureCapacity方法实现的。当元素数量超过了数组容量时,该方法会调用grow方法来扩容数组。 ``` private void ensureCapacityInternal(int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity); } private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); } 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); } ``` 首先,ensureCapacityInternal方法会调用ensureExplicitCapacity方法,该方法会检查是否需要扩容,并在需要时调用grow方法。 grow方法会首先计算新的数组容量,它的计算方式是将原来的容量增加一半。然后,grow方法会检查新容量是否大于最大数组容量,如果是,则调用hugeCapacity方法来返回一个足够大的容量值。最后,grow方法会调用Arrays.copyOf方法来将原来的数组复制到新的数组中。 需要注意的是,在进行扩容操作时,ArrayList会创建一个新的数组,并将原来的元素复制到新的数组中。这个过程会占用一定的时间和空间,因此,在使用ArrayList时,应该尽量避免频繁地进行扩容操作,以提高性能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值