Java集合框架学习---深入探究ArrayList源码(一)

第一部分 ArrayList简介

ArrayList是java.util包下面AbstractList类的一个子类。涉及到的类和接口具体关系如下:

Java集合框架学习---深入探究ArrayList源码(一)_第1张图片
ArrayList UML图

值得一提的是,类AbstractCollection是实现了Collection接口的,所以ArrayList可以算得上是Collection接口的一个典型的实现类(因为平常用得比较多)。

List接口的大小可变数组的实现,实现了所有可选列表操作,并允许包括null在内的所有元素。除了实现List接口之外,此类(ArrayList)还提供了一些方法来操作内部用来存储列表的数组的大小(此类大致上等同于Vector类,但它是不同步的)。

size、isEmpty、get、set、iterator和listIterator操作都以固定时间运行。add操作以分摊的固定时间运行,也就是说添加n个元素需要O(n)时间。其他所有操作都以线性时间运行(大体上讲)。与用于LinkedList实现的常数因子相比,此实现的常数因子较低。

每个ArrayList实例都有一个容量。该容量是指用来存储列表元素的数组的大小。它总是至少等于列表的大小。随着向ArrayList中不断添加元素,其容量也自动增长,并未指定增长策略的细节,因为这不只是添加元素会带来分摊固定时间开销那么简单。

在添加大量元素之前,应用程序可以使用ensureCapacity操作来增加ArrayList实例的容量,这可以减少递增式再分配的容量。

要注意的是,ArrayList不是同步的
如果多个线程同时访问一个ArrayList实例,而其中至少一个线程从结构上修改了列表,那么它必须保持外部同步(结构上的修改是指任何添加或删除一个或多个元素的操作,或者显式调整底层数组的大小;仅仅设置元素的值不是结构上的修改)。这一般通过对自然封装该列表的对象进行同步操作来完成。如果不存在这样的对象,则应该使用Collections.synchronizedList方法将该列表"包装"起来。这最好在创建时完成,以防止意外对列表进行不同步的访问:

List list = Collections.synchronizedList(new ArrayList(...));

此类的iterator和listIterator方法返回的迭代器是快速失败的:在创建迭代器之后,除非通过迭代器自身的removeadd方法从结构上对列表进行修改,否则在任何时间以任何方式对列表进行修改,迭代器都会抛出ConcurrentModificationException。因此,面对并发的修改,迭代器很快就会完全失败,而不是冒着将来某个不确定时间发生任意不确定行为的风险。

需要注意的是,迭代器的快速失败行为无法得到保证,因为一般来说,不可能对是否抛出不同步并发修改做出任何硬性保证。快速失败迭代器会尽最大努力抛出ConcurrentModificationException。因此,为提高这类迭代器的正确性而编写一个依赖此程序的程序是错误的做法:迭代器的快速失败行为应该仅用于检测bug。

ArrayList 是一个数组队列,相当于 动态数组。与Java中的数组相比,它的容量能动态增长。它继承于AbstractList,实现了List, RandomAccess, Cloneable, java.io.Serializable这些接口。
ArrayList 继承了AbstractList,实现了List。它是一个数组队列,提供了相关的添加、删除、修改、遍历等功能。ArrayList 实现了RandmoAccess接口,即提供了随机访问功能。RandmoAccess是java中用来被List实现,为List提供快速访问功能的。在ArrayList中,我们即可以通过元素的序号快速获取元素对象;这就是快速随机访问。稍后,我们会比较List的“快速随机访问”和“通过Iterator迭代器访问”的效率。
ArrayList 实现了Cloneable接口,即覆盖了函数clone(),能被克隆。
ArrayList 实现java.io.Serializable接口,这意味着ArrayList支持序列化,能通过序列化去传输。

和Vector不同,ArrayList中的操作不是线程安全的!所以,建议在单线程中才使用ArrayList,而在多线程中可以选择Vector或者CopyOnWriteArrayList。

第二部分 ArrayList源码探究(基于jdk1.8.0_74)

package java.util;

import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
/** 概述: List接口可调整大小的数组实现。实现所有可选的List操作,并允许所有元素,包括null,
元素可重复。  除了列表接口外,该类提供了一种方法来操作该数组的大小来存储该列表中的数组的大小。

 时间复杂度:  方法size、isEmpty、get、set、iterator和listIterator的调用是常数时间的。 
 添加删除的时间复杂度为O(N)。其他所有操作也都是线性时间复杂度。 
 
容量: 每个ArrayList都有容量,容量大小至少为List元素的长度,默认初始化为10。 
 容量可以自动增长。 如果提前知道数组元素较多,
可以在添加元素前通过调用ensureCapacity()方法提前增加容量以减小后期容量自动增长的开销。 
 也可以通过带初始容量的构造器初始化这个容量。 

 线程不安全:  ArrayList不是线程安全的。如果需要应用到多线程中,需要在外部做同步 

modCount:  定义在AbstractList中:protected transient int modCount = 0; 
已从结构上修改此列表的次数。从结构上修改是指更改列表的大小,或者打乱列表,
从而使正在进行的迭代产生错误的结果。
此字段由iterator和listiterator方法返回的迭代器和列表迭代器实现使用。
如果意外更改了此字段中的值,则迭代器(或列表迭代器)将抛出concurrentmodificationexception
来响应next、remove、previous、set或add操作。 
在迭代期间面临并发修改时,它提供了快速失败 行为,而不是非确定性行为。
子类是否使用此字段是可选的。  如果子类希望提供快速失败迭代器(和列表迭代器),
则它只需在其 add(int,e)和remove(int)方法
(以及它所重写的、导致列表结构上修改的任何其他方法)中增加此字段。
 对add(int, e)或remove(int)的单个调用向此字段添加的数量不得超过 1,
否则迭代器(和列表迭代器)将抛出虚假的 concurrentmodificationexceptions。 
 如果某个实现不希望提供快速失败迭代器,则可以忽略此字段。

transient:  默认情况下,对象的所有成员变量都将被持久化.
在某些情况下,如果你想避免持久化对象的一些成员变量,
你可以使用transient关键字来标记他们,transient也是java中的保留字(JDK 1.8) 
*/
public class ArrayList extends AbstractList
        implements List, RandomAccess, Cloneable, java.io.Serializable
{
    private static final long serialVersionUID = 8683452581122892189L;

   
    private static final int DEFAULT_CAPACITY = 10;//默认初始容量

  
    private static final Object[] EMPTY_ELEMENTDATA = {};//用于空实例的共享空数组对象实例

 
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

   
    transient Object[] elementData; // non-private to simplify nested class access


    private int size;

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

  
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

   
    public ArrayList(Collection c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
          
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
       
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }
    public void trimToSize() {
        modCount++;
        if (size < elementData.length) {
            elementData = (size == 0)
              ? EMPTY_ELEMENTDATA
              : Arrays.copyOf(elementData, size);
        }
    }


    public void ensureCapacity(int minCapacity) {
        int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
            // any size if not default element table
            ? 0
            // larger than default for default empty table. It's already
            // supposed to be at default size.
            : DEFAULT_CAPACITY;

        if (minCapacity > minExpand) {
            ensureExplicitCapacity(minCapacity);
        }
    }

    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 static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

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

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

 
    public int size() {
        return size;
    }

    
    public boolean isEmpty() {
        return size == 0;
    }

   
    public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }

  
    public int indexOf(Object o) {
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

  
    public int lastIndexOf(Object o) {
        if (o == null) {
            for (int i = size-1; i >= 0; i--)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = size-1; i >= 0; i--)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }


    public Object clone() {
        try {
            ArrayList v = (ArrayList) super.clone();
            v.elementData = Arrays.copyOf(elementData, size);
            v.modCount = 0;
            return v;
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw new InternalError(e);
        }
    }

  
    public Object[] toArray() {
        return Arrays.copyOf(elementData, size);
    }

    
    @SuppressWarnings("unchecked")
    public  T[] toArray(T[] a) {
        if (a.length < size)
            // Make a new array of a's runtime type, but my contents:
            return (T[]) Arrays.copyOf(elementData, size, a.getClass());
        System.arraycopy(elementData, 0, a, 0, size);
        if (a.length > size)
            a[size] = null;
        return a;
    }

    // Positional Access Operations

    @SuppressWarnings("unchecked")
    E elementData(int index) {
        return (E) elementData[index];
    }

  
    public E get(int index) {
        rangeCheck(index);

        return elementData(index);
    }

   
    public E set(int index, E element) {
        rangeCheck(index);

        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }

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

   
    public void add(int index, E element) {
        rangeCheckForAdd(index);

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

   
    public E remove(int index) {
        rangeCheck(index);

        modCount++;
        E oldValue = elementData(index);

        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }

    public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

  
    private void fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work
    }

  
    public void clear() {
        modCount++;

        // clear to let GC do its work
        for (int i = 0; i < size; i++)
            elementData[i] = null;

        size = 0;
    }

    public boolean addAll(Collection c) {
        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount
        System.arraycopy(a, 0, elementData, size, numNew);
        size += numNew;
        return numNew != 0;
    }


    public boolean addAll(int index, Collection c) {
        rangeCheckForAdd(index);

        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount

        int numMoved = size - index;
        if (numMoved > 0)
            System.arraycopy(elementData, index, elementData, index + numNew,
                             numMoved);

        System.arraycopy(a, 0, elementData, index, numNew);
        size += numNew;
        return numNew != 0;
    }


    protected void removeRange(int fromIndex, int toIndex) {
        modCount++;
        int numMoved = size - toIndex;
        System.arraycopy(elementData, toIndex, elementData, fromIndex,
                         numMoved);

        // clear to let GC do its work
        int newSize = size - (toIndex-fromIndex);
        for (int i = newSize; i < size; i++) {
            elementData[i] = null;
        }
        size = newSize;
    }

    private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

    /**
     * A version of rangeCheck used by add and addAll.
     */
    private void rangeCheckForAdd(int index) {
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }


    private String outOfBoundsMsg(int index) {
        return "Index: "+index+", Size: "+size;
    }

    public boolean removeAll(Collection c) {
        Objects.requireNonNull(c);
        return batchRemove(c, false);
    }

    public boolean retainAll(Collection c) {
        Objects.requireNonNull(c);
        return batchRemove(c, true);
    }

    private boolean batchRemove(Collection c, boolean complement) {
        final Object[] elementData = this.elementData;
        int r = 0, w = 0;
        boolean modified = false;
        try {
            for (; r < size; r++)
                if (c.contains(elementData[r]) == complement)
                    elementData[w++] = elementData[r];
        } finally {
            // Preserve behavioral compatibility with AbstractCollection,
            // even if c.contains() throws.
            if (r != size) {
                System.arraycopy(elementData, r,
                                 elementData, w,
                                 size - r);
                w += size - r;
            }
            if (w != size) {
                // clear to let GC do its work
                for (int i = w; i < size; i++)
                    elementData[i] = null;
                modCount += size - w;
                size = w;
                modified = true;
            }
        }
        return modified;
    }

    private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException{
        // Write out element count, and any hidden stuff
        int expectedModCount = modCount;
        s.defaultWriteObject();

        // Write out size as capacity for behavioural compatibility with clone()
        s.writeInt(size);

        // Write out all elements in the proper order.
        for (int i=0; i

The {@code Spliterator} reports {@link Spliterator#SIZED}, * {@link Spliterator#SUBSIZED}, and {@link Spliterator#ORDERED}. * Overriding implementations should document the reporting of additional * characteristic values. * * @return a {@code Spliterator} over the elements in this list * @since 1.8 */ @Override public Spliterator spliterator() { return new ArrayListSpliterator<>(this, 0, -1, 0); } /** Index-based split-by-two, lazily initialized Spliterator */ static final class ArrayListSpliterator implements Spliterator { private final ArrayList list; private int index; // current index, modified on advance/split private int fence; // -1 until used; then one past last index private int expectedModCount; // initialized when fence set /** Create new spliterator covering the given range */ ArrayListSpliterator(ArrayList list, int origin, int fence, int expectedModCount) { this.list = list; // OK if null unless traversed this.index = origin; this.fence = fence; this.expectedModCount = expectedModCount; } private int getFence() { // initialize fence to size on first use int hi; // (a specialized variant appears in method forEach) ArrayList lst; if ((hi = fence) < 0) { if ((lst = list) == null) hi = fence = 0; else { expectedModCount = lst.modCount; hi = fence = lst.size; } } return hi; } public ArrayListSpliterator trySplit() { int hi = getFence(), lo = index, mid = (lo + hi) >>> 1; return (lo >= mid) ? null : // divide range in half unless too small new ArrayListSpliterator(list, lo, index = mid, expectedModCount); } public boolean tryAdvance(Consumer action) { if (action == null) throw new NullPointerException(); int hi = getFence(), i = index; if (i < hi) { index = i + 1; @SuppressWarnings("unchecked") E e = (E)list.elementData[i]; action.accept(e); if (list.modCount != expectedModCount) throw new ConcurrentModificationException(); return true; } return false; } public void forEachRemaining(Consumer action) { int i, hi, mc; // hoist accesses and checks from loop ArrayList lst; Object[] a; if (action == null) throw new NullPointerException(); if ((lst = list) != null && (a = lst.elementData) != null) { if ((hi = fence) < 0) { mc = lst.modCount; hi = lst.size; } else mc = expectedModCount; if ((i = index) >= 0 && (index = hi) <= a.length) { for (; i < hi; ++i) { @SuppressWarnings("unchecked") E e = (E) a[i]; action.accept(e); } if (lst.modCount == mc) return; } } throw new ConcurrentModificationException(); } public long estimateSize() { return (long) (getFence() - index); } public int characteristics() { return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED; } } @Override public boolean removeIf(Predicate filter) { Objects.requireNonNull(filter); // figure out which elements are to be removed // any exception thrown from the filter predicate at this stage // will leave the collection unmodified int removeCount = 0; final BitSet removeSet = new BitSet(size); final int expectedModCount = modCount; final int size = this.size; for (int i=0; modCount == expectedModCount && i < size; i++) { @SuppressWarnings("unchecked") final E element = (E) elementData[i]; if (filter.test(element)) { removeSet.set(i); removeCount++; } } if (modCount != expectedModCount) { throw new ConcurrentModificationException(); } // shift surviving elements left over the spaces left by removed elements final boolean anyToRemove = removeCount > 0; if (anyToRemove) { final int newSize = size - removeCount; for (int i=0, j=0; (i < size) && (j < newSize); i++, j++) { i = removeSet.nextClearBit(i); elementData[j] = elementData[i]; } for (int k=newSize; k < size; k++) { elementData[k] = null; // Let gc do its work } this.size = newSize; if (modCount != expectedModCount) { throw new ConcurrentModificationException(); } modCount++; } return anyToRemove; } @Override @SuppressWarnings("unchecked") public void replaceAll(UnaryOperator operator) { Objects.requireNonNull(operator); final int expectedModCount = modCount; final int size = this.size; for (int i=0; modCount == expectedModCount && i < size; i++) { elementData[i] = operator.apply((E) elementData[i]); } if (modCount != expectedModCount) { throw new ConcurrentModificationException(); } modCount++; } @Override @SuppressWarnings("unchecked") public void sort(Comparator c) { final int expectedModCount = modCount; Arrays.sort((E[]) elementData, 0, size, c); if (modCount != expectedModCount) { throw new ConcurrentModificationException(); } modCount++; } }

以上是ArrayList的源码,下面我将对其源码进行深入探究

  • 继承的类以及实现的接口(上面已经提过了)
public class ArrayList extends AbstractList
        implements List, RandomAccess, Cloneable, java.io.Serializable
  • 成员变量
private static final long serialVersionUID = 8683452581122892189L;//版本号
private static final int DEFAULT_CAPACITY = 10;//默认容量
/*
  *默认空Object数组, 用于定义空的ArrayList
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/* 也是空数组,
  * 但是是在新增元素的时候使用
  */
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
transient Object[] elementData;//ArrayList中用来存储元素的地方,数组长度就是它的容量
private int size;//ArrayList所包含的元素的数量,即ArrayList的大小

数组elementData为ArrayList存储元素的Buffer,ArrayList的容量即为该数组的长度。当ArrayList为空时,此时elementData==EMPTY_ELEMENTDATA,当往ArrayList添加一个元素时,elementData的长度就被初始化为10,也就是DEFAULT_CAPACITY的值。
注意到elementData数组被transient修饰,因为ArrayList实现了Serializable接口,这意味着ArrayList可以被序列化,也就是说,ArrayList的属性可以通过网络传输,或者被存储到磁盘持久化。但是在很多情况下,我们并不希望有些属性被传输或者存储,比如一些敏感信息。这时使用trainsent标记属性,就可以使得ArrayList在序列化的过程中elementData不参与序列化。因此,elementData中的数据仅存在于调用者的内存中(关于transient这点在后面还会提)

  • 构造函数
    由于ArrayList是Collection的实现类,所以它像Collection其它的通用实现类一样,除了拥有自己特有的构造方法之外,还提供两个“标准”构造方法,故有三个构造方法:
  1. 无参数构造方法:
public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

无参构造方法直接创建一个大小为10的数组
ps:在jdk1.5以前,无参的构造方法是下面这样的

public ArrayList(){  
    this(10);  
}  
  1. 带有 Collection 类型单参数的构造方法:
/*
  *构造一个包含指定元素的list,这些元素的是按照Collection的迭代器返回的顺序排列的
*/
 public ArrayList(Collection 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;
        }
    }

用于创建一个具有与其参数相同元素新的 collection。实际上,它允许用户复制任何 collection,以生成所需实现类型的一个等效 collection。
换句话说,就是将提供的集合转换成数组返回给elementData(返回的不是Object[],那么将使用Arrays.copyOf将其转换成Object[])。如果该集合大小为零,则返回一个默认的数组。

  1. ArrayList特有的构造方法
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);
        }
    }

如果传入的参数initialCapacity大于零,那么就为elementData生成一个大小为initialCapacity的数组。如果initialCapacity等于零,那么就生成一个默认数组。如果initialCapacity小于零,则抛出一个非法参数的错误。

  • trimToSize函数:
public void trimToSize() {
        modCount++;
        if (size < elementData.length) {
            elementData = (size == 0)
              ? EMPTY_ELEMENTDATA
              : Arrays.copyOf(elementData, size);
        }
    }

将此 ArrayList实例的容量调整为列表的当前大小---如果此ArrayList的size为零,那就返回一个默认的数组,如果size不为零,那就将容量调整为列表的当前大小。在应用中通常使用此方法来最大限度地降低一个ArrayList所使用的存储空间。

下面我将用一个小程序来看看trimToSize函数到底是如何工作的:

import java.util.ArrayList;

public class Eakon{
    public static void main(String[] args){
        //创建一个大小为10的NumberList
    ArrayList NumberList = new ArrayList(10);
    //往里面添加15个数字
    for(int i = 0; i < 15; i++){
        NumberList.add(i);
        }
    NumberList.add(1);
    //使用trimToSize函数
    NumberList.trimToSize();
    }
}

接下来是Debug的过程:

Java集合框架学习---深入探究ArrayList源码(一)_第2张图片
carried line 6

如上图所示,执行完第6行代码之后生成了一个大小为10的数组,然后我们继续往下执行,即进入循环:

Java集合框架学习---深入探究ArrayList源码(一)_第3张图片
往NumberList添加了9个元素之后
Java集合框架学习---深入探究ArrayList源码(一)_第4张图片
往NumberList添加了11个元素之后

可以看到,由于ArrayList的自动扩容,本来初始化时大小为10的NumberList在添加到第11个元素的时候(此时modCount值为11)自动大小自动扩容到了15(至于如何实现自动扩容我们以后再学习)。那么我们继续执行下去,直到执行完trimToSize函数:

Java集合框架学习---深入探究ArrayList源码(一)_第5张图片
执行完第11行代码之后,NumberList里面只有16个元素,但是它的大小为22
Java集合框架学习---深入探究ArrayList源码(一)_第6张图片
执行完trimToSize函数之后

我们可以看到,当执行完trimToSize函数之后,NumberList的大小变为了它实际存储的元素的数量。这样一来,我们就不难理解trimToSize函数所发挥的作用了。

由于对文章长度有所限制,所以接下来的内容我另开一篇博客继续介绍。

链接:Java集合框架学习---深入探究ArrayList源码(二)

你可能感兴趣的:(Java集合框架学习---深入探究ArrayList源码(一))