ArrayList源碼分析

ArrayList是在實際項目中一個很是經常使用的類,今天咱們經過源碼來了解一下ArrayList的本質java

內部變量說明

//默認容量
    private static final int DEFAULT_CAPACITY = 10;

	//使用有參構造建立ArrayList的時候,若是size=0那麼elementData就會指向這個數組,或者調用trimToSize(0)方法也會指向這個空數組
    private static final Object[] EMPTY_ELEMENTDATA = {};

	//只有使用無參構造的時候纔會將elementData指向這個空數組
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

	//內部的元素數組,咱們使用get()方法其實就是從其中取出元素
    transient Object[] elementData; // non-private to simplify nested class access

	//ArrayList的長度,默認爲0
    private int size;

	//ArrayList的臨界值,若是下一次擴容的數值大於這個數值,那麼將elementData擴容到數組的最大值也就是0x7fffffff,若是最小須要的容量已經大於0x7fffffff那麼則拋出OOM異常
	private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
複製代碼

初始化

ArrayList()

咱們要使用ArrayList一定要先new一個出來,如今咱們來看看new 到底作了寫什麼.算法

ArrayList<Object> arrayList = new ArrayList<>();
複製代碼

在咱們調用這樣一個空參構造的時候,ArrayList實際上幫咱們建立了一個空的數組,口說無憑,能夠來看看數組

/** * 構造一個空list而且初始化容量爲10(若是隻是單純的new實際上並無初始化容量) * Constructs an empty list with an initial capacity of ten. */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
複製代碼

而這個 DEFAULTCAPACITY_EMPTY_ELEMENTDATA是什麼呢?ui

/** * 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 = {};
複製代碼

是的,他就是一個長度爲0的Object的對象數組. 這個是一個 默認的靜態被final修飾的的空數組實例,每個array list實例被new出來一開始都共享這個空數組實例.this

ArrayList(int initialCapacity)和ArrayList(Collection<? extends E> c)

當咱們調用這兩個方法中的其中一個方法的時候,若是 initialCapacity==0或者c.size()==0那麼內部的element將會指向EMPTY_ELEMENTDATAspa

問題:不是已經有了一個空數組(DEFAULTCAPACITY_EMPTY_ELEMENTDATA)了爲何還要有一個空數組(EMPTY_ELEMENTDATA)呢?

用一個場景來解釋這個問題code

在咱們建立ArrayList 的時候,若是使用ArrayList<>(int initialCapacity)指定初始化容量的方式來建立,這時候傳參是0那麼,ArrayList內部的容器指向的就是這個EMPTY_ELEMENTDATAcdn

在執行add方法的時候,若是是使用無參構造的方式建立的ArrayList 那麼就會直接初始化其容量爲10.對象

若是是使用有參構造的方式建立的ArrayList那麼就會執行正常的擴容方式blog

說白了就是,DEFAULTCAPACITY_EMPTY_ELEMENTDATA就是一個標記,用來區分ArrayList是否是無參構造建立的ArrayList

1557540713333

添加元素

void add(E e)

當咱們添加元素的時候只須要調用add方法就好了,可是你知道其背後到底執行了哪些動做嗎?讓咱們接着往下看吧!

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

咱們來看看流程

流程

看了流程以後,你可能會有諸多問題.

究竟是怎麼計算擴容的容量的?

添加元素是怎麼添加的?

擴容容量是怎麼計算的?

此處以**void add(E e)**爲例

當咱們添加一個元素的時候,若是這個元素是經過無參構造建立的(爲了避免讓思路變得更復雜,此處只針對void add(E e)方法添加元素的容量計算),那麼就將其容量初始化爲 DEFAULT_CAPACITY,也就是10(還記得開始咱們介紹了其內部的變量嗎,就是那個DEFAULT_CAPACITY)

看起來這樣作就足夠了,可是java爲了不溢出,又作了一層處理

新容量= 原來的容量+原來的容量/2 這塊有兩層判斷,

newCapacity-minCapacity<0 newCapacity-MAX_ARRAY_SIZE>0 (同newCapacity-Integer.MAX_VALUE - 8>0 )

第二個其實還想得通當超過最大數組長度的時候就將其擴展到Integer.MAX_VALUE

那第一個新的容量爲何會小於最小的容量呢?

其實是由於Integer超過最大範圍再加會溢出直接變爲-2^32.

元素是如何添加的?

從新建立一個新的數組,將其靠拷貝

System.arraycopy()

獲得元素

E get(int index)

  • 此方法會先檢查index是不是有效的索引
  • 是有效的則會從內部數組elementData中強轉而且返回該元素
  • 不是有效的則會拋出IndexOutOfBoundsException異常

結語:

  • ArrayList是基於數組實現的.
  • ArrayList的擴容算法是 oldLength+oldLength>>1,也就是oldLength+oldLength/2
  • ArrayList是擴容是從新分配一片空間,將原來的內容拷貝進去.
  • ArrayList理論上的最大長度是Integer.MAX_VALUE
相關文章
相關標籤/搜索