ArrayList原始碼分析筆記
阿新 • • 發佈:2021-03-06
# ArrayList原始碼分析筆記
### 先貼出ArrayList一些屬性
```java
public class ArrayList extends AbstractList
implements List, RandomAccess, Cloneable, java.io.Serializable
{
/**
* 系列化ID.
*/
private static final long serialVersionUID = 8683452581122892189L;
/**
* 預設初始化容量.
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* 用於空例項的共享空陣列例項.
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* 共享的空陣列例項,用於預設大小的空例項。我們將此與EMPTY_ELEMENTDATA區別開來,
以瞭解新增第一個元素時需要膨脹多少。
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* 儲存ArrayList的元素的陣列緩衝區。 ArrayList的容量是此陣列緩衝區的長度。新增第
一個元素時,任何具有elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA的空
ArrayList都將擴充套件為DEFAULT_CAPACITY。
*/
transient Object[] elementData;
/**
* ArrayList的大小(它包含的元素數).
*
* @serial
*/
private int size;
```
以上屬性註釋都已經被翻譯成中文,通過這些註釋,我們大概能瞭解到的一些有價值的資訊
- **ArrayList底層資料結構是一個Object陣列**
- **ArrayList的預設初始化容量為10**
- **一個空的ArrayList集合在新增第一個元素時被擴容為10**
- **ArrayList的大小是通過一個名為size的int變數儲存的**
原始碼繼續往下看,先看ArrayList的建構函式
```java
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的無參建構函式時,這個ArrayList的Object陣列被初始化為 DEFAULTCAPACITY_EMPTY_ELEMENTDATA=10
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* Constructs a list containing the elements of the specified
* collection, in the order they are returned by the collection's
* iterator.
*
* @param c the collection whose elements are to be placed into this list
* @throws NullPointerException if the specified collection is null
*/
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;
}
}
```
可以看到ArrayList一共有三個建構函式,一個無參構造,兩個有參構造。
當我們在建立一個Arraylist集合時,如果不指定容量,也就是呼叫無參建構函式,那麼這個Arraylist會被初始化為10.
```java
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
```
另外注意到另外兩個有參構造,一個引數為``int initialCapacity``,也就是我們指定的初始化容量,這裡對這個initialCapacity進行了一些簡單的驗證
```Java
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {//initialCapacity > 0
this.elementData = new Object[initialCapacity];//將一個容量為initialCapacity的新Object陣列賦給elementData。
} else if (initialCapacity == 0) {//initialCapacity==0
this.elementData = EMPTY_ELEMENTDATA;//將EMPTY_ELEMENTDATA這個空object賦給elementData
} else {//其他情況,也就是小於0,直接拋異常
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
```
另一個有參構造
```java
public ArrayList(Collection extends E> c) {
elementData = c.toArray();//將Collection集合轉化為陣列,然後賦給elementData這個object陣列
if ((size = elementData.length) != 0) {//如果這個集合長度不為0
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)//如果集合轉化後的陣列不是Object陣列
elementData = Arrays.copyOf(elementData, size, Object[].class);//轉化為Object陣列
} else {//其他情況,也就是elementData的長度為0嘛
// 替換為EMPTY_ELEMENTDATA空的陣列
this.elementData = EMPTY_ELEMENTDATA;
}
}
```
可以看到這個建構函式,它是直接把一個Collection丟了進去,這也就是說,**在new一個ArrayList時我們可以把一個Collection集合放到引數列表中。**
接下來再來看ArrayList的add方法
```java
/**
* 將元素新增到列表的末尾
*
* @param e 要被追加到list中的元素
* @return true (as specified by {@link Collection#add})
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;//將元素e新增到陣列中,下標為size++,其實也就是size,然後新增後size+1,
return true;
}
```
這裡就兩行程式碼,呼叫``ensureCapacityInternal``這個方法,並且引數為size+1。點進去看下``ensureCapacityInternal()``這個方法
```java
/*
通過這個方法我們可以看出,當我們第一次呼叫add方法的時候,elementData陣列的size為0,那麼size+1就為1,所以minCapacity也為1,這裡先通過一個if判斷,判斷minCapacity是不是一個空的object陣列,如果是的話,minCapacity就取DEFAULT_CAPACITY和minCapacity的最大值,也就是10嘛
*/
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
//判斷完之後進入了這個方法,同時引數為minCapacity,如果是第一次呼叫add方法的話引數為minCapacity就是預設大小10,不是第一次呼叫的話,就是size+1,
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
//這個grow()就是擴容函式,現在minCapacity的話,還是一樣的分析,如果第一次呼叫add方法的話就是10,同時elementData.length為0,不是第一次呼叫的話minCapacity就是size+1,elementData.length也就是陣列的長度為size
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
```
讀完這一段原始碼,可以得出以下結論
- 當我們建立ArrayList時,如果不指定ArrayList的大小,那麼第一次呼叫add方法時,底層會呼叫擴容方法grow()對Arraylist進行擴容
- 當ArrayList裡面已經存滿了元素的時候,再呼叫add方法,此時會底層會呼叫grow方法進行擴容,比如ArrayList的大小為10,裡面也已經有10個元素了,當我們再呼叫add方法向裡面新增元素的時候,此時Arraylist會觸發擴容。
接下來我們再來看下grow方法,瞭解一些Arraylist到底是怎麼樣擴容的
```java
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity > > 1);
//如果陣列當前長度+陣列當前長度/2 < 陣列當前元素個數+1
if (newCapacity - minCapacity < 0)
//就把陣列當前元素個數+1賦給newCapacity
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 最後我們可以看到,這裡進行了一個複製操作,將elementData裡面的元素,複製到一個新的長度為newCapacity的Object陣列,然後再把它賦給elementData
elementData = Arrays.copyOf(elementData, newCapacity);
}
```
這段程式碼應該很清晰吧,grow方法傳進來的引數就是上面minCapacity,第一次呼叫add方法時是10,其他時候呼叫時是size+1(當前儲存元素個數+1)
這裡簡單來說就是將elementData當前長度給oldCapacity,然後newCapacity = oldCapacity + oldCapacity >> 1(左移操作,相當於除於2),如果oldCapacity = 10的話,newCapacity = 10 +(10/2),也就是15。通過以上程式碼,不難看出,ArrayList的擴容方法實際上就是將底層Object陣列複製到了一個新的長度更長的數組裡面去,而這個新陣列的長度是``原來的陣列長度+原來陣列長度的二分之一``,其實就可以說了擴容了1.5倍。
通過對以上原始碼進行分析,我們可以得出以下結論了
總結:
- **ArrayList底層資料資料結構是一個Object型別的陣列**
- **ArrayList的預設初始化容量為10**
- **當我們在建立ArrsyList時不指定ArrsyList初始大小,第一次呼叫add方法時擴容為初始化大小10**
- **ArrayList的大小是通過一個名為size的int變數儲存的**
- **ArrayList有一個建構函式允許Collection集合作為引數,並且會將這個Collection集合轉化為陣列**
- **ArrayList在新增第``elementData.length+1``個元素時會發生擴容,比如陣列長度為10,在新增第11個元素時擴容**
- **ArrayList擴容大小為原來的1.5倍,底層實現是通過``原來陣列長度+原來陣列長度左移1位``完成,而不是直接通過乘法**