1. 程式人生 > >ArrayList源碼解析(一)

ArrayList源碼解析(一)

unary 定義 cte 轉換 ora gif 成員類 con ins

目錄

  • 1.位置
  • 2.變量和常量
  • 3.構造函數
  • 4.trimToSize()方法

正文

源碼解析系列主要對Java的源碼進行詳細的說明,由於水平有限,難免出現錯誤或描述不準確的地方,還請大家指出。

回到頂部

1.位置

ArrayList位於java.util包中。

1 package java.util;
2 
3 import java.util.function.Consumer;
4 import java.util.function.Predicate;
5 import java.util.function.UnaryOperator;

回到頂部

2.變量和常量

先明確一點,ArrayList是采用Object類型的數組實現的。

ArrayList開始定義了一些常量和變量:

技術分享
private static final long serialVersionUID = 8683452581122892189L;

    /**
     * Default initial capacity.
* ArrayList初始化默認容量大小為10,見無參構造函數。 */ private static final int DEFAULT_CAPACITY = 10; /** * Shared empty array instance used for empty instances.
* 空實例共享的空數組實例。 */ private static final Object[] EMPTY_ELEMENTDATA = {}; /** * 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.
* 默認大小的空實例共享的空數組實例。將它與EMPTY_ELEMENTDATA區分開,以便確定當添加了第一個成員之後擴充多大。
*/
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; 

/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
* ArrayList成員存儲在這個數組緩沖區中。
* 容量大小是這個緩沖區的長度,任何elementDate==DEFAULTCAPACITY_EMPTY_ELEMENTDATA的空ArrayList
* 在添加第一個成員時都會擴充到DEFAULT_CAPACITY大小,即容量為10。
*/

transient Object[] elementData; // non-private to simplify nested class access

/**
* The size of the ArrayList (the number of elements it contains).
* ArrayList的大小,註意是實際包含的成員個數。
* @serial
*/
private int size;
技術分享 回到頂部

3.構造函數

ArrayList有三個構造函數:

1)無參構造函數

技術分享
    /**
     * Constructs an empty list with an initial capacity of ten.
* 構造一個初始容量為10的空list。 */ public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
技術分享

註意,這裏說構造一個初始容量為10的空list,但是賦給elementData的只是一個空的數組(原因後續說明)。

2)帶指定初始容量參數的構造函數

技術分享
 /**
     * Constructs an empty list with the specified initial capacity.
     * 構造一個指定了初始容量的空list。
     * @param  initialCapacity  the initial capacity of the list
* initialCapacity指定list的初始容量 * @throws IllegalArgumentException if the specified initial capacity * is negative
* 當指定容量值為負值時,拋出IllegalArgumentException異常 */ 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); } }
技術分享

當指定的初始容量值大於零時,新建一個大小為initialCapacity的Object數組,並賦 elementData;

當指定的初始容量值為零時,將 EMPTY_ELEMENTDATA賦給elementData。

3)使用指定Collection構造ArrayList的構造函數

技術分享
    /**
     * Constructs a list containing the elements of the specified
     * collection, in the order they are returned by the collection‘s
     * iterator.
     * 構造一個包含指定Collection成員的list,成員的排列順序與使用Collection的
* iterator返回的順序一致。 * @param c the collection whose elements are to be placed into this list
* c 指定的Collection,它的所有元素都會被放在構造的list中 * @throws NullPointerException if the specified collection is null
* 當指定的Collection為null是拋出NullPointerException異常 */ public ArrayList(Collection<? extends E> c) { elementData = c.toArray(); if ((size = elementData.length) != 0) { // 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; } }
技術分享

既然是從指定的Collection構造ArrayList,那麽成員的類型就要保持不變,即ArrayList中的成員類型與Collection中的成員類型要相同。

先利用toArray()將Collection c轉換為數組,並讓elementData指向這個數組;

接下來,取出elementData的成員個數並賦值給size;

如果size不為0,則判斷elementData的成員類是否與Object類相同,不同則進行一次copy操作,並進行類型轉換;

如果size為0,則將elementData指向EMPY_ELEMENTDATA。

回到頂部

4.trimToSize()方法

技術分享
/**
     * Trims the capacity of this <tt>ArrayList</tt> instance to be the
     * list‘s current size.  An application can use this operation to minimize
     * the storage of an <tt>ArrayList</tt> instance.
* 將ArrayList實例的容量trim為list的當前size。
* 應用可以使用這個方法來最小化ArrayList實例的存儲空間。 */ public void trimToSize() { modCount++; if (size < elementData.length) { elementData = (size == 0) ? EMPTY_ELEMENTDATA : Arrays.copyOf(elementData, size); } }
技術分享

modCount用來記錄修改次數,modCount主要用於多線程環境下,因為ArrayList是非線程安全的,這裏使用了Fail-Fast機制。任何對ArrayList的修改都會增加modCount的值,在叠代器初始化過程中會將這個值賦給叠代器的 expectedModCount。在叠代過程中,會判斷 modCount是否等於 expectedModCount,如果不等,說明其他線程修改了ArrayList,就要拋出ConcurrentModificationException 異常。

理解這個函數的前提是分清楚size和length的不同之處:

size是ArrayList實際包含的成員個數;而length是ArrayList的容量,即最多容納多少元素。

比如ArrayList的length可以為10,但是只包含6個成員,其余為null。

trimToSize()方法的目的就是使length等於size,以節約存儲空間。

當size小於elementData的length時:

如果size為0,說明 elementData 不包含任何成員,把elementData指向 EMPTY_ELEMENTDATA 即可;

如果size不為0,說明elementData包含成員,這時調用copyOf(),做一次copy,使ArrayList的容量大小和size相等(這裏主要目的是理解ArrayList,暫時不對copyOf()進行說明,後續文章詳細介紹)。

下篇文章我們主要分析ArrayList的擴容機制。

ArrayList源碼解析(一)