Java集合源碼分析系列-(一)ArrayList源碼剖析

前言

今天介紹常常使用的一個Java集合類——ArrayList(基於JDK1.8.0_121)。ArrayList在工做和平常面試中常常被使用或者提到。總的來講,工做中使用ArrayList主要是由於動態數組的方便性,面試中出現ArrayList常常是和LinkedList/Vector一塊兒出現,分析這三種集合的異同。java

ArrayList類圖

ArrayList類圖

圖片是直接從IntelliJ中導出來的,其中:藍色線條意味着繼承,綠色線條意味着接口實現。面試

ArrayList源碼剖析

ArrayList定義

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable

咱們首先須要明白而且牢記在心裏的是,ArrayList本質上是一個數組,可是與Java中基礎的數組所不一樣的是,它可以動態增加本身的容量
經過ArrayList的定義,能夠知道ArrayList繼承了AbstractList,同時實現了List,RandomAccess,Cloneable和java.io.Serializable接口。數組

繼承了AbstractList類,實現了List,意味着ArrayList是一個數組隊列,提供了諸如增刪改查、遍歷等功能。
實現了RandomAccess接口,意味着ArrayList提供了隨機訪問的功能。RandomAccess接口在Java中是用來被List實現,用來提供快速訪問功能的。在ArrayList中,即咱們能夠經過元素的序號快速獲取元素對象。
實現了Cloneable接口,意味着ArrayList實現了clone()函數,能被克隆。
實現了java.io.Serializable接口,意味着ArrayList可以經過序列化進行傳輸。dom

ArrayList關鍵屬性

private static final int DEFAULT_CAPACITY = 10;
transient Object[] elementData;
private int size;

(1)ArrayList的默認容量爲10;
(2)elementData是"Object類型的數組",全部ArrayList元素都保存在elementData中。在ArrayList中,elementData是一個動態數組。須要注意的是,ArrayList經過構造函數ArrayList(int initialCapacity)定義初始量initialCapacity;
(3)size是動態數組的實際大小。函數

//ArrayList帶容量的構造函數
public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
    //新建一個容量爲initialCapacity的數組
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}
//ArrayList默認構造函數,默認爲空
public ArrayList() {

    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
// 構造一個包含指定元素的list
public ArrayList(Collection<? extends E> 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;
    }
}

第一個構造方法使用提供的initialCapacity來初始化elementData數組的大小。
第二個構造方法默認數組爲0。
第三個構造方法則將提供的集合轉成數組返回給elementData(返回若不是Object[]將調用Arrays.copyOf方法將其轉爲Object[])。this

ArrayList主要方法源碼剖析

增長

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

public void add(int index, E element) {
    //判斷index是否越界,錯誤產生IndexOutOfBoundsException
    rangeCheckForAdd(index);
    //進行擴容檢查
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    //對數組進行復制,將空出的Index位置出入element,並將index後的全部數據後移一個位置。
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
    //將index上的數據設置爲element
    elementData[index] = element;
    //容量+1
    size++;
}

刪除

public E remove(int index) {
    //邊界檢查
    rangeCheck(index);
        
    modCount++;
    //oldValue即要刪除的元素
    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) {
    //對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;
}

更新

public E set(int index, E element) {
    //數組擴容
    rangeCheck(index);
    //獲取要更新的位置的數據
    E oldValue = elementData(index);
    //更新元素
    elementData[index] = element;
    return oldValue;
}

總結

1.ArrayList底層是經過數組來保存數據的。默認的容量是10.

2.JDK1.8中,ArrayList擴容使用位運算newCapacity = oldCapacity + (oldCapacity >> 1)

3.ArrayList實現java.io.Serializable的方式。當寫入到輸出流時,先寫入「容量」,再依次寫入「每個元素」;當讀出輸入流時,先讀取「容量」,再依次讀取「每個元素」。

相關文章
相關標籤/搜索