ArrayList源码详解

必要的初始知识

System.arraycopy这个东西最好要知道一下,这是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(负数)
     */
      // 用初始长度去声明一个空的列表(list),initialCapacity固初始化长度,IllegalArgumentException如果给的值不合理(负数),就抛出这个错误.
    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.
     *创建一个列表(list)包含一个已经指定元素的集合,用由那个集合的迭代器按顺序返回
     * @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();//将输入的集合化为数组--Object[]数组.
        //如果输入的集合不为空
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            //对于这段话的解释https://www.cnblogs.com/gilbertbright/p/11714334.html
            if (elementData.getClass() != Object[].class)
                //如果输入的内容不是object类型,则要按照那个类型去进行赋值.
                //original - 要复制的数组 
               //newLength - 要返回的副本的长度 
               //newType - 要返回的副本的类 
                //将elementData到Object数组中,即让他们的类型产生变化
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

c.toArray might (incorrectly) not return Object[] (see 6260652)是什么意思

https://www.cnblogs.com/liqing-weikeyuan/p/7922306.html 这个也是个很好的问题

本类的成员变量

   private static final long serialVersionUID = 8683452581122892189L;

    /**
     * Default initial capacity.初始化长度.  
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * Shared empty array instance used for empty instances.
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};//如果故意在有参构造函数中传0用的数组.

    /**
     * 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//ArrayList的底层函数

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

java集合-ArrayList中EMPTY_ELEMENTDATA与DEFAULTCAPACITY_EMPTY_ELEMENTDATA的区别

缩减函数

/**
 * Trims the capacity of this <tt>ArrayList</tt> instance to be the
 * list's current size.  An application can use this operation to minimize
 * the storage of an <tt>ArrayList</tt> instance.
 */
//缩减这个ArrayList实例的容量道它的最优大小.一个程序可以用这个操作去缩小一个ArrayList实例的大小.
public void trimToSize() {
    modCount++;
    if (size < elementData.length) {
        //如果size为0就给他变回空数组,如果不是空数组,则复制它的数组,截掉那部分值为0的尾部
        1,2,3,4,0,0,0->1,2,3,4
        elementData = (size == 0)
          ? EMPTY_ELEMENTDATA
          : Arrays.copyOf(elementData, size);
    }
}

计算最小扩容长度函数(预备)

/**
 * Increases the capacity of this <tt>ArrayList</tt> instance, if
 * necessary, to ensure that it can hold at least the number of elements
 * specified by the minimum capacity argument.
 *
 * @param   minCapacity   the desired minimum capacity//最小需求容量
 */
//增加这个ArrayList的容量,如果是不必要的,保证这个ArrayList中的元素数量是最小容量.
//在扩容的时候使用的函数,保证扩容增长的长度是最小的长度.
public void ensureCapacity(int minCapacity) {
    //最小扩展量
    int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
        // any size if not default element table
        //如果不是固定的那个空数组,最小的扩展就是0
        ? 0
        // larger than default for default empty table. It's already
        // supposed to be at default size.
        //如果传入的数比默认的空数组大,也被建议先采用默认值.
        : DEFAULT_CAPACITY;
//如果最小需求容量大于最小扩展值(最小扩展值实际上只在空ArrayList被第一次加入元素的时候才有用处[用处是直接把容量从0加到10],其他时候没用)
    if (minCapacity > minExpand) {
        //确定准确的ArrayList容量是什么.
        ensureExplicitCapacity(minCapacity);
    }
}

如上函数被addAll()用到的



如下函数是被add()用到的


//确定这个最小容量的大小.
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    //如果elementData还是最小初始数组   
    <---2.被 ensureCapacityInternal调用,返回值给ensureExplicitCapacity
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
       //看看这个最小容量大还是规定的默认长度(10)大,返回大的那个
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}
//如果
private void ensureCapacityInternal(int minCapacity) {  <---1.被add()方法调用
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
//确定准确的ArrayList容量是什么.
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

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

扩容函数

/**
 * 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
 */
//增加容量去确保ArrayList拥有至少由最小容量给定的容量.
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;//老板长度
    int newCapacity = oldCapacity + (oldCapacity >> 1);//old+old/2
    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);
}

增加函数

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

上面三个函数要一同看

当调用一个add()方法的时候首先运行的是 ensureCapacityInternal(size + 1);方法,判断ArrayList是否需要扩容.在这个函数中会先调用private static int calculateCapacity(Object[] elementData, int minCapacity) 函数,去对增加的数量进行判断.

对于用空参构造函数构造的ArrayList,为了节省空间实际上使用的是一个空数组,并没长度(并不是10).所以在这个函数中要判断增加的元素数量是否大于10(也就是ArrayList的默认长度),如果不大于10,用10作为容量,反之用传入的那个长度作为容量.

然后调用private void ensureExplicitCapacity(int minCapacity)在这里又进行了另一次判断,这里要注意传入的容量(minCapacity)未必大于ArrayList的现有长度.,所以在这里要有一次判断判断是否应该要调用grow()方法进行扩大.

一个toArray()方法

public <T> T[] toArray(T[] a) {
        if (a.length < size)
            // Make a new array of a's runtime type, but my contents:
            //用传入的值去更改这个ArrayList中的所有内容
            //Arrays.copyOf复制指定的数组,用空值截断或填充(如有必要),以便复制具有指定的长度。
            return (T[]) Arrays.copyOf(elementData, size, a.getClass());
        System.arraycopy(elementData, 0, a, 0, size);
        if (a.length > size)
            a[size] = null;//如果进来的数组长度大于ArrayList长度,则将数组中最后一个设置为空.
        return a;//返回数组
    }

一个get()方法

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

    return elementData(index);
}
  E elementData(int index) {
        return (E) elementData[index];
    }//返回index位置上的索引的值

get()方法是由两个函数组成.目标是更加的简洁

set()方法也是大体如此

public E set(int index, E element) {
    rangeCheck(index);//确定没有超出边界

    E oldValue = elementData(index);//保留原先的值
    elementData[index] = element;//覆盖新的值
    return oldValue;//将原先的值返回
}

增加函数

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

    ensureCapacityInternal(size + 1);  // Increments modCount!!//判断是否需要扩容,上面讲过了.
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);//从index位置上的元素开始将后面的所有元素(总长度size-索引位置index)挪到index+1的位置上
    elementData[index] = element;//把值放在index索引处
    size++;//长度计数器增加.
}

减小函数

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

    modCount++;
    E oldValue = elementData(index);//取出原先的值

    int numMoved = size - index - 1;//因为index上的值消失,所以实际上是从index+1开始的,也正是因此要多减一个
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);//从index+1开始复制,一直到数组解为,粘贴到index开始的位置上
    //此时会有两个最后一个元素,让他为空
    elementData[--size] = null; // clear to let GC do its work
    //如果一个对象所有的引用都为空,则与GC-root断链,会被JVM标记,然后清除.
    return oldValue;
}

add All()

public boolean addAll(Collection<? extends E> c) {
    Object[] a = c.toArray();//将传入的集合化为数组,每个集合中的元素都会被擦除成Object类型.
    int numNew = a.length;
    ensureCapacityInternal(size + numNew);  // Increments modCount
    System.arraycopy(a, 0, elementData, size, numNew);//从a(传入数组)的0位置复制该数组长度,从size开始粘贴到ArrayList维护的底层数组.
    size += numNew;
    return numNew != 0;
}

这个本质来讲没啥区别,但是重点是用到了ensureCapacityInternal(size + numNew); 这个方法,在上面的模块中,这个一直没用用到.请回到上面去看一下.注意minCapacity为最小需求容量.上面可能有些出入,请记住这个意思.

如上就是我们经常用到的ArrayList方法的详解.

还有一部分方法,大多原理和如上几个方法重复,或者平时基本不用.就不再写出来了.

posted @ 2020-05-15 16:52  TimothyRasinski  阅读(355)  评论(0编辑  收藏  举报