Java-ArrayList源碼分析

Java-ArrayList源碼分析

參考

  • JDK 源碼

Overview

ArrayList是咱們很是經常使用的一個集合,那麼ArrayList是如何實現呢?java

從一個小Demo開始分析

ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("Hello");
if (arrayList.contains("Hello")) {
    System.out.println("[Hello] is in ArrayList!");
}
arrayList.remove("Hello");
複製代碼

構造函數

public ArrayList() {
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    //存儲數據的集合
    transient Object[] elementData; // non-private to simplify nested class access
    //....
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
複製代碼

從構造函數能夠看出,如今咱們的集合是一個空集合,真正存儲數據的數組也是一個長度爲0的數組。數組

Add方法

public boolean add(E e) {
    modCount++;
    add(e, elementData, size);
    return true;
}

private void add(E e, Object[] elementData, int s) {
    //存儲數據的數組已經所有被使用
    if (s == elementData.length)
        //擴張數組
        elementData = grow();
    elementData[s] = e;
    size = s + 1;
}
複製代碼

一個參數的add方法調用了private的add方法,在該方法中真正完成了向集合中添加元素。在添加元素的時候,若是size==存儲數據的數組的長度,那麼就代表數組已經存滿了,這時候就須要將本來的數組進行擴張,如何擴張數組是經過 grow 方法來實現的。函數

Grow方法

private Object[] grow() {
    return grow(size + 1);
}

private Object[] grow(int minCapacity) {
    //複製當前數組到一個新的數組中(新的數組長度已經擴張過)
    return elementData = Arrays.copyOf(elementData,
                                       newCapacity(minCapacity));
}

//計算新的擴張長度
private int newCapacity(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    //計算新的容量

    //新容量= 數組的長度+數組的長度向右位移1(例如:10>>1 = 5, 11>>1 = 5)
    //這種新容量的計算方式代表:當集合的長度越大時,集合每一次的擴張的幅度就會愈來愈大
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    //當新容量小於最小容量時,以最小容量爲基準
    if (newCapacity - minCapacity <= 0) {
        //當數組中沒有元素的時候,會執行這個分支
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
            //返回最小容量和默認容量中較大的一個
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return minCapacity;
    }
    
    return (newCapacity - MAX_ARRAY_SIZE <= 0)
        ? newCapacity
        : hugeCapacity(minCapacity);//極端狀況,能夠不考慮(幾乎不會走到這個分支)
}

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

Grow 方法是實現ArrayList長度可變的核心,其實現思想是源碼分析

當數組存放滿了的時候,就擴張數組(當集合中的元素的數量越多的時候,擴張的幅度就越大。) 而擴張數組是經過,創建新的數組(長度等於擴張後的長度),而後將舊的數組中的元素填充到新數組中,這種方式來實現的。this

Contains方法

Contains也是ArrayList集合很是經常使用的一個方法,用來判斷集合中是否包含指定的元素。spa

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

public int indexOf(Object o) {
    return indexOfRange(o, 0, size);
}

int indexOfRange(Object o, int start, int end) {
    Object[] es = elementData;
    if (o == null) {
        for (int i = start; i < end; i++) {
            if (es[i] == null) {
                return i;
            }
        }
    } else {
        for (int i = start; i < end; i++) {
            if (o.equals(es[i])) {
                return i;
            }
        }
    }
    return -1;
}
複製代碼

contains方法最終調用了調用了indexOfRange方法,indexOfRange作的工做是,在集合指定的範圍內判斷是否包含指定的元素,若是包含就返回下標,不然就返回-1.code

remove方法

public boolean remove(Object o) {
    final Object[] es = elementData;
    final int size = this.size;
    int i = 0;
    //搜尋指定元素
    found: {
        if (o == null) {
            for (; i < size; i++)
                if (es[i] == null)
                    break found;
        } else {
            for (; i < size; i++)
                if (o.equals(es[i]))
                    break found;
        }
        return false;
    }
    //移除元素
    fastRemove(es, i);
    return true;
}

private void fastRemove(Object[] es, int i) {
    modCount++;
    final int newSize;
    //若是不是在數組的末尾移除元素
    if ((newSize = size - 1) > i)
        //將i以後全部元素向前移動一個下標
        System.arraycopy(es, i + 1, es, i, newSize - i);
    es[size = newSize] = null;
}
複製代碼

remove方法的邏輯很清晰:ci

  1. 首先找到須要移除的元素的下標
  2. 判斷下標是不是在集合的中間
  3. 若是下標在集合的中間,那麼代表須要將該下標後面的元素所有向前移動一位下標。
  4. 將數組的最後一個元素設置爲null

總結

ArrayList經過數組實現了可變集合,可是咱們從源碼中能夠看出來,若是要增長刪除元素的話,是很是消耗資源和時間的(由於在頻繁的操做數組),可是訪問的時候是特別的快的(直接經過下標訪問便可)。element

能夠得出結論: ArrayList不適合頻繁的增長刪除,可是適合查詢。資源

相關文章
相關標籤/搜索