ArrayList源码:add 方法

  • Post author:
  • Post category:其他




1. add(E e):

// 向列表中追加一个元素。
// size:当前列表中元素的数量。
public boolean add(E e) {
    // size+1 表示最小容量。最小容量 == 当前列表中元素个数+1。
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    // 到这里已经扩容结束了。将新元素追加到数组。
    elementData[size++] = e;
    // 添加成功,返回 true。
    return true;
}

看下扩容函数:

ensureCapacityInternal(size + 1)

private void ensureCapacityInternal(int minCapacity) {
    // 1.先计算出所需的最小容量。
    // 2.确认最小容量,并执行扩容。
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

(1)计算所需的最小容量。如果是向空列表中 add 元素,最小容量取

默认容量(10)



待添加的元素个数

中较大的那个。如果是向非空列表中 add 元素,最小容量直接取:

当前列表中元素的个数 + 待添加的元素的个数

// 计算需要的最小容量。minCapacity == 当前数组元素个数 + 要添加的元素的个数。
// DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}
// DEFAULT_CAPACITY = 10
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    // 向空数组中添加元素,比如第一次 add 元素时才会触发
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        // 返回 默认容量 和 minCapacity 中最大的。
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    // 向非空数组 add 时,直接返回 minCapacity。
    return minCapacity;
}

(2.1)确认是否扩容。只有当 计算所需要的最小容量 大于 当前数组的长度时,才扩容。

// minCapacity 是计算出来的最小容量。
private void ensureExplicitCapacity(int minCapacity) {
    // 这个是校验并发用的。
    modCount++;

    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        // 当需要的最小容量 > 当前数组的长度时, 执行扩容。
        grow(minCapacity);
}

(2.2) 执行扩容。

// minCapacity 是所需的细小容量。
private void grow(int minCapacity) {
    // 当前数组的长度。
    int oldCapacity = elementData.length;
    // 扩容到当前数组长度的 1.5 倍
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    // 如果扩容后的容量还小于所需的最小容量,那就以计算需要的为准(再将容量扩大)。
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    // 再判断,如果新容量是个很大的数子,快逼近 int 的极限的了,但还没有溢出,
    // 那就直接将新容量扩大到 int 的极限。
    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) {
    // 如果容量超 int 范围了,报异常。
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    // 三段式,确定大容量的准确值。
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
    MAX_ARRAY_SIZE;
}



2. addAll(Collection<? extends E> c)

// 向列表中追加一个list。
public boolean addAll(Collection<? extends E> c) {
    // 拿出 list 中元素保存到数组中。
    Object[] a = c.toArray();
    // 获取追加的元素个数。
    int numNew = a.length;
    // 给原本的 elementData 扩容。
    ensureCapacityInternal(size + numNew);  // Increments modCount
    // 将 a 数组中的元素从 0 开始,依次复制到 elementData 的 size 位置开始,并往后推
    // 总共复制 numNew 个。
    System.arraycopy(a, 0, elementData, size, numNew);
    // 更新列表长度。
    size += numNew;
    return numNew != 0;
}



3. add(int index, E element)

   // 在指定索引出插入元素。
   public void add(int index, E element) {
       // 检查是否越界。
       rangeCheckForAdd(index);
       // 扩容。
       ensureCapacityInternal(size + 1);  // Increments modCount!!
       // 将 index 位置以及后面的元素,向后移动 1 为。
       System.arraycopy(elementData, index, elementData, index + 1,
                        size - index);
       // 在指定位置插入元素。
       elementData[index] = element;
       // 列表中元素个数 + 1。
       size++;
   }



4. addAll(int index, Collection<? extends E> c)

   // 在指定索引出插入一个集合。
   public boolean addAll(int index, Collection<? extends E> c) {
       // 检查索引是否越界。
       rangeCheckForAdd(index);
       // 集合里的元素保存到数组中。
       Object[] a = c.toArray();
       // 数组长度。
       int numNew = a.length;
       // 扩容。
       ensureCapacityInternal(size + numNew);  // Increments modCount
       // 原数组中要向后移动的元素的个数。
       int numMoved = size - index;
       // 如果向后移动的元素的个数大于 0。
       if (numMoved > 0)
           // index 位置开始向后,总共 numMoved 元素,均向后移动 numNew 的距离。
           System.arraycopy(elementData, index, elementData, index + numNew,
                            numMoved);
       // 将 a 中的元素从 elementData 的 index 位置开始,其次复制到 elementData 中。
       System.arraycopy(a, 0, elementData, index, numNew);
       // 列表中总元素个数更新。
       size += numNew;
       return numNew != 0;
   }



5. ArrayList() 无参构造函数。

private static final int DEFAULT_CAPACITY = 10;

public ArrayList() {
    // 创建空数组,此时 ArrayList 对象默认的 capacity = 10。
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}



6. 小问题

public class MyArrayListAdd {

  public static void main(String[] args){
    ArrayList<String> demo = new ArrayList<>();
    demo.add("diego");
    demo.add("amos");
  }
}

三行代码,每执行一行,列表容量和数组的数组的长度是分别是多少?

ArrayList<String> demo = new ArrayList<>();

默认容量:10 ,空数组长度是:0

demo.add("diego");

向空列表中 add 元素,需求容量是 1,默认容量是 10 ,取较大的,所以计算的最小容量是 10。因为 最小容量大于当前数组长度,要扩容,最终确认的容量也是 10,因此创建长度为 10 的数组,最后向里面追加一个元素。

容量:10,数组长度:10,但里面只有一个元素。

demo.add("amos");

向非空列表中 add 元素,需求容量是 2,所以计算出来的最小容量直接是 2。因为计算出来的最小容量小于当前数组的长度。不需要扩容。直接在向数组中追加新元素。

容量:2,数组长度 10,里面有两个元素。



7. Vector 和 ArrayList 的区别是什么?


Vector 是线程安全版的 ArrayList。

对比一下 add 方法的源码就能看清楚了

// Vector.java

// synchronized 加锁 
  public synchronized boolean add(E e) {
      modCount++;
      ensureCapacityHelper(elementCount + 1);
      elementData[elementCount++] = e;
      return true;
  }
// ArrayList.java
public boolean add(E e) {
    ensureCapacityInternal(size + 1); 
    elementData[size++] = e;
    return true;
}



版权声明:本文为yy_diego原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。