[Java集合]------ArrayList的扩容机制源码分析

一、ArrayList的简介

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

ArrayList实现了List<E>、RandomAccess、Cloneable、java.io.Serializable接口

  1. Lits<E>:说明ArrayList是一个有序可重复的集合
  2. RandomAccess:说明ArrayList具备随机访问功能[RandomAccess更像是一种标识,代表你具备随机访问功能,但并不代表着实现了RandomAccess接口就具备随机访问功能]
  3. Cloneable:说明List支持拷贝
  4. Serializable:序列化标识,说明List可以通过序列化方式实现深拷贝

二、ArrayList成员变量

/**
     * Default initial capacity.
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * Shared empty array instance used for empty instances.
     */
    private static final Object[] 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 = {};

    /**
     * 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;
  1. DEFAULT_CAPACITY : 初始化容量大小,默认为10
  2. EMPTY_ELEMENTDATA :空数组
  3. DEFAULTCAPACITY_EMPTY_ELEMENTDATA :也是一个空数组,与EMPTY_ELEMENTDATA的区别就是用来判断第一次添加元素时,需要对数组扩充多少,默认的构造函数返回的就是这个空数组
  4. elementData:用于存储ArrayList元素的数组缓冲区。ArrayList的容量是此数组缓冲区的长度。添加第一个元素时,任何具有elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA 的空ArrayList都将扩展为DEFAULT_CAPACITY,即容量大小扩展为10
  5. size:当前数组长度

三、ArrayList的构造方法

/**
     * 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;
        }
    }
  1. ArrayList():默认构造方法,返回DEFAULTCAPACITY_EMPTY_ELEMENTDATA空列表
  2. ArrayList(int initialCapacity):返回指定数组容量大小的列表,当initialCapacity为0时,返回EMPTY_ELEMENTDATA
  3. ArrayList(Collection<? extends E> c):返回具有指定集合元素的列表,当c的容量为0时,返回EMPTY_ELEMENTDATA

四、ArrayList扩容机制

当调用add方法的时候,会先调用ensureCapacityInternal方法,判断是否需要扩容

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

在ensureCapacityInternal方法中确认minCapacity的值

  1. 首先会判断当前的数组elementData是否为DEFAULTCAPACITY_EMPTY_ELEMENTDATA[当ArrayList是通过无参构造函数实例化时,elementData=DEFAULTCAPACITY_EMPTY_ELEMENTDATA]
  2. 若相等,则minCapacity的值为DEFAULT_CAPACITY和minCapacity两者的最大值,第一次新增的时候,minCapacity的值为size+1,即1,DEFAULT_CAPACITY的值为10,所以此时minCapacity为10
  3. 若不相等,则minCapacity的值为size+1
  4. 调用ensureExplicitCapacity进行下一步的扩容操作
    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }

在ensureExplicitCapacity方法中[暂时不讨论modCount的作用],判断当前数组长度是否大于最小的扩容容量

  1. 若当前数组长度elementData.length小于minCapacity,则表示需要扩容,执行grow方法
  2. 否则不需要扩容
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

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

在grow方法中为具体的扩容逻辑,通过当前数组的长度,计算出扩容后的数组长度。具体公式为oldCapacity + (oldCapacity >> 1)。

有如下三种情况

  1. 当前数组长度为0[使用自定义容量构造函数或者使用指定集合[传入的集合大小为0]的构造函数时],即elementData.length=0,则计算后的数组长度newCapacity为0+0>>1=0。若newCapacity小于minCapacity,数组长度扩至minCapacity,即从0扩到1,以此类推,需要经历4次[0->1,1->2,2->3,3->4]后,第五次才会开始1.5倍的扩容
  2. 当前数组长度为10[使用无参构造函数创建ArrayList,且第一次add元素时],即elementData.length=10,则计算后的数组长度newCapacity为10+10>>1=15,此时相当于扩容1.5倍
  3. 若计算出来的扩容大小newCapacity大于MAX_ARRAY_SIZE [2的31次方-9],此时会调用hugeCapacity方法来校验。当所需的最小容量大小大于MAX_ARRAY_SIZE,则newCapacity=Integer.MAX_VALUE,否则newCapacity=MAX_ARRAY_SIZE。由此可知,ArrayList的最大大小为Integer.MAX_VALUE
 /**
     * 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);
    }

五、快速失败机制fail-fast

快速失败机制是Java集合中一种错误检测机制。在迭代集合的过程中,出现结构上的改变时[比如大小改变],就会通过快速失败机制检测出来,并抛出ConcurrentModificationException的错误,而快速失败机制就是通过变量modCount来判断是否需要抛出ConcurrentModificationException的。

下面以ArrayList的迭代器遍历ArrayList并在过程中删除元素为例子看看modCount是如何实现快速失败机制的

Iterator<String> iterator = list.iterator();

iterator方法是List接口的方法,ArrayList实现List接口后,会实例化自己的iterator,在调用list.iterator()后,ArrayList会new一个Itr对象并返回

 /**
     * Returns an iterator over the elements in this list in proper sequence.
     *
     * <p>The returned iterator is <a href="#fail-fast"><i>fail-fast</i></a>.
     *
     * @return an iterator over the elements in this list in proper sequence
     */
    public Iterator<E> iterator() {
        return new Itr();
    }

这个Itr对象是ArrayList的一个内部类,该类实现了Iterator接口,里面有三个成员变量

  1. cursor:表示下一个要返回的元素的下标
  2. lastRet:表示当前遍历到的元素的下标,默认为-1,即cursor-1
  3. expectedModCount:fail-fast的关键参数,默认值为modCount。因为modCount是属于AbstarctList中的,初始值为0,所以ArrayList在没有任何操作的时候,modCount的值为0,故此expectedModCount的值也为0

最主要的三个方法为hasNext、next和remove

  1. hasNext:判断是否遍历完所有元素,当下一个元素的下标cursor等于数组长度时,表示所有元素均遍历完了
  2. next:获取下一个元素
  3. remove:删除当前遍历到的元素,此时会修改modCount的值
int cursor;       // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;

public boolean hasNext() {
            return cursor != size;
        }

        @SuppressWarnings("unchecked")
        public E next() {
            checkForComodification();
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }

        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

在每次调用next方法的时候,首先会调用checkForComodification方法,该方法通过modCount与expectedModCount的值是否相等来判断当前集合结构是否发生了改变,若发生了改变,则抛出ConcurrentModificationException异常。由于在调用hasNext和next方法的时候,并没有修改modCount的值,所以正常情况下modCount=expectedModCount

final void checkForComodification() {
      if (modCount != expectedModCount)
         throw new ConcurrentModificationException();
}

而当调用ArrayList的add、remove等方法时,会修改modCount的值,所以在使用迭代器遍历的过程中,调用ArrayList的add、remove等方法时,通过快速失败机制,会抛出ConcurrentModificationException异常

使用Iterator的remove方法时,由于该方法没有修改modCount的值,所以并不会抛出ConcurrentModificationException异常。这也是为什么在遍历集合元素的过程中,若想动态删除集合的时候,要用迭代器的remove,而不能使用集合的remove

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值