【转载】ArrayList从源码看扩容实现

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

ArrayList类继承自抽象类AbstractList,实现了List接口,随机存取RandomAccess接口,克隆接口Cloneable,序列化接口Serializable。

通常,我们使用Arraylist添加一个对象往往无需过多考虑,直接add()即可,无需担心其容量是否超过限制,其原因就在于Arraylist源码中,实现add()方法之前会查看其容量是否够用,不够则会自动‘扩容’然后再执行add()添加元素,即自动扩容。

让我们从一个空的数组列表添加元素开始,看一下源码是如何实现的。

List list = new ArrayList();
list.add("A");

首先,List list = new ArrayList();初始化了一个空数组列表list,具体是ArrayList类源码中:

public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
*/
transient Object[] elementData; // non-private to simplify nested class access

/**
* The size of the ArrayList (the number of elements it contains).
*
* @serial
*/
private int size;

初始化ArrayList对象的同时创建了一个默认为空的Object数组变量elementData。然后list.add("A")对应的是源码中的add()方法:

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

如图中所示,public boolean add(E e){}方法会执行modCount++;然后调用add(e,elementData,size)方法,最后返回布尔类型的ture。

modCount在ArrayList中定义如下:

private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}

doc说明:

/**
* The number of times this list has been <i>structurally modified</i>.
* Structural modifications are those that change the size of the
* list, or otherwise perturb it in such a fashion that iterations in
* progress may yield incorrect results....

很多结构化地改变数组的操作都会用到modCount计数,用来标识数组被修改的次数。譬如add , set , remove等。

由于是初次添加元素,故modCount++后变为1,来到下一步add(e, elementData, size),此时e为要添加的元素:“A”,elementData为刚刚创建的Object[], size为数组初始容量,值为0(在ArrayList中定义:private int size,int型初始未赋值则为0)

重点看下add方法的具体实现:

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

由于初始elementData.length==size==0故执行grow()方法,返回值赋给elementData。

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

grow有两个重载的方法,无参的grow()会调用有参的grow,参数为size+1。

private Object[] grow(int minCapacity) {
        return elementData = Arrays.copyOf(elementData, newCapacity(minCapacity));
    }

此处size+1=1作为minCapacity,表示最小容量,实际也很好理解,因为add一个对象首先我需要的最小容量是原长度+1,有了最小容量才能进行下一步地添加元素操作,所以通过grow()来确保我arraylist对象的长度,即【扩容】

扩容的操作通过Arrays.copyOf方法实现:

public static <T> T[] copyOf(T[] original, int newLength) {
return (T[]) copyOf(original, newLength, original.getClass());
}

此方法直接将原数组elementData复制到一个新的给定长度的数组中,并返回此新数组对象。

那么新数组长度是多少呢?这个就是newCapacity()中定义的,让我们看一下:

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(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}

http://1.int oldCapacity = elementData.length

此处由于elementData是空数组,所以oldCapacity==elementData.length==0

2.newCapacity = oldCapacity + (oldCapacity >> 1)

这一步是扩容的核心操作,即扩容后新数组的长度=原数组的容量+原数组的容量/2.

通过oldCapacity >> 1(Java移位运算,右移1位相当于除以2)来实现扩容原数组容量的一半

,即ArrayList的扩容是根据原容量的1.5倍来扩容的。

但是此时oldCapacity值为0故newCapacity也为0,扩容无效...

3.if(newCapacity - minCapacity <=0)

经过第二步后的newCapacity=0,小于minCapacity=1故进入下面的步骤:

public static native void arraycopy(Object src,  int  srcPos,   Object dest, int destPos,  int length);

4.return Math.max(DEFAULT_CAPACITY, minCapacity);

DEFAULT_CAPACITY为ArrayList中初始的默认容量,大小为10。定义如下:

return Math.max(DEFAULT_CAPACITY, minCapacity)返回10和minCapacity中的最大值,此处直接返回10

现在,经过newCapacity()计算后的新数组容量为10,让我们回到grow()中的:

Arrays.copyOf将elementData复制到一个容量为10的新Object[]中去,并返回此数组对象elementData,完成了前面一大堆准备工作,并执行扩容后,终于来到了add()方法中的最后两步:

elementData[s] = e;

完成新增元素添加

size = s + 1;

将该ArrayList对象的数量+1

 

转载于:https://www.cnblogs.com/smallwangmusk/p/11421232.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ArrayListJava中常用的动态数组实现类,它可以自动扩容以容纳更多的元素。下面我们来看一下ArrayList扩容源码。 首先,ArrayList内部维护了一个数组elementData,用于存储元素。当我们向ArrayList中添加元素时,如果元素个数已经达到了数组的容量,就需要对数组进行扩容。下面是ArrayList扩容的核心代码: ```java private void ensureCapacityInternal(int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity); } private void ensureExplicitCapacity(int minCapacity) { modCount++; if (minCapacity - elementData.length > 0) grow(minCapacity); } private void grow(int minCapacity) { int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); elementData = Arrays.copyOf(elementData, newCapacity); } ``` 在ensureCapacityInternal方法中,首先判断当前的elementData是否是默认空数组,如果是,则将minCapacity与默认容量DEFAULT_CAPACITY取最大值。这是因为默认空数组的容量为0,如果minCapacity小于DEFAULT_CAPACITY,就会直接使用默认容量。如果不是默认空数组,则直接调用ensureExplicitCapacity方法。 在ensureExplicitCapacity方法中,首先将modCount加1,表示对ArrayList进行了一次修改。然后判断需要扩容的最小容量是否大于当前数组的容量,如果是,就调用grow方法进行扩容。 在grow方法中,首先将当前数组的容量oldCapacity扩大1.5倍,得到新的容量newCapacity。然后判断新容量是否小于需要的最小容量,如果是,就将新容量设置为需要的最小容量。最后再判断新容量是否超过最大数组容量MAX_ARRAY_SIZE,如果超过,就调用hugeCapacity方法抛出异常。 最后,通过调用Arrays.copyOf方法将原数组elementData复制到新数组中,完成扩容。 总体来说,ArrayList扩容过程就是先判断是否需要扩容,然后计算新容量,最后将原数组复制到新数组中。需要注意的是,ArrayList扩容策略是每次扩容1.5倍,而不是每次扩容一个固定的值。这是因为这种策略可以在大多数情况下减少扩容次数,提高性能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值