天天看點

jdk1.8 Arraylist源碼分析remove(int index)

一、概述

ArrayList是開發時經常使用到的集合類,它是用數組實作的,具有通路快,增删慢的特點。本文主要對ArrayList的常用方法進行分析。

二、變量

/**
 * 預設的初始容量大小
 */
private static final int DEFAULT_CAPACITY = 10;

/**
 * 容量為0時,elementData的值
 */
private static final Object[] EMPTY_ELEMENTDATA = {};

/**
 * 使用無參構造函數時,elementData的預設值
 */
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

/**
* 存放元素的數組
 */
transient Object[] elementData; 

/**
 * 元素數量
 */
private int size;
           

三、構造函數

ArrayList中有三個構造函數,這三個構造函數都是public修飾的,是以有3種方式建立ArrayList對象。

無參構造函數

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

參數為整型變量

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

這裡的邏輯比較簡單,主要是校驗參數,然後為ArrayList設定初始容量,将elementData 初始化為指定長度的數組。initialCapacity等于0的時候,将elementData設定為EMPTY_ELEMENTDATA,initialCapacity小于0時抛出不支援的參數異常。

參數為Collection<? extends E>類型變量

public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();
    if ((size = elementData.length) != 0) {
        // 6260652是jdk的bug編号
        // 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;
    }
}
           

此構造函數構造一個包含指定集合的元素的清單。需要注意的是c.toArray方法,在注釋中說,c.toArray不一定會傳回Oject類型的數組,使用Arrays.asList(T… a)構件一個List時,傳回的對象是Arrays的靜态内部類Arrays.ArrayList,它存放元素不是用的Object數組 。是以需要校驗數組的類型,這是一個jdk中的bug,bug編号是6260652,可以到Oracle的官方網站中檢視關于這個bug的資訊https://bugs.java.com/bugdatabase/view_bug.do?bug_id=6260652

四、主要方法

add(E e)方法

/**
 * 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) {
	//保證elementData的大小能夠存放新元素
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}
           

add()方法執行邏輯為:

1.保證elementData的大小能夠存放新元素,如果elementData數組放滿時,就會進行elementData的擴容。

2.将新元素存放到elementData數組中

進入ensureCapacityInternal方法

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

    ensureExplicitCapacity(minCapacity);
}
           

方法中的判斷elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA就是使用預設構造函數初始化ArrayList時,将minCapacity 設定為預設初始化大小10,也就是将elementData數組的大小設定為10。

進入ensureExplicitCapacity方法

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

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

modCount是modifyCount的簡寫,記錄修改次數,将修改次數+1,elementData數組放滿時,執行grow(minCapacity)進行擴容操作。

進入grow方法

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方法的邏輯為:

1.将容量大小擴容為原大小的1.5倍。

2.如果newCapacity不滿足存放元素的最小容量時,将newCapacity 設定為minCapacity。

3.如果newCapacity大于MAX_ARRAY_SIZE(Integer.MAX_VALUE - 8)時,通過hugeCapacity(minCapacity)方法擴容。

4.将elementData中的元素複制到新數組中。完成擴容

add(int index, E element)方法

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

此方法的邏輯為:先對index參數進行檢查,然後進行進入ensureCapacityInternal方法,看是否進行擴容,然後将elemenData數組index位置以及index之後的所有元素都向後移動一個位置,讓index位置為null,然後将element放到index位置。

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

System.arraycopy方法是一個native方法,是由c語言實作。src是原數組對象,srcPos是原數組對象的開始位置,dest是目标對象,destPos是目标對象的開始位置,length是複制的長度。此方法的作用是将指定數組src從指定位置srcPos複制到目标數組dest的指定位置destPos。 複制的元件數量等于length參數。即将源數組src中srcPos至srcPos+length-1的對象分别複制到目标數組dest的destPos至destPos+length-1 位置。

remove(int index)

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

主要邏輯為:

1、檢查index參數

2、修改記錄+1

3、擷取到index位置的對象

4、将index位置之後的所有對象向前移動一個位置

5、将最後一個位置的對象設定為null以便能夠被垃圾回收器清理