ArrayList源碼解讀
ArrayList底層是數組實現的
首先了解幾個必要的成員變量
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
private static final int DEFAULT_CAPACITY = 10; //默認容量大小爲10
private static final Object[] EMPTY_ELEMENTDATA = {};//內容放在這裏,剛開始這個數組對象沒有內容。當用戶指定該 ArrayList 容量爲 0 時,返回該空數組
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; //默認的空的沒有任何內容的數組。當用戶沒有指定 ArrayList 的容量時(即調用無參構造函數),返回的是該數組
/*
ArrayList基於數組實現,用該數組保存數據, ArrayList 的容量就是該數組的長度
- 該值爲 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 時,當第一次添加元素進入 ArrayList 中時,數組將擴容值 DEFAULT_CAPACITY(10)
*/
transient Object[] elementData;
private int size;//記錄容器有效數據個數
}
DEFAULTCAPACITY_EMPTY_ELEMENTDATA
與 EMPTY_ELEMENTDATA
的區別就是:DEFAULTCAPACITY_EMPTY_ELEMENTDATA
數組是默認返回的,而後者是在用戶指定容量爲 0 時返回
1.ArrayList()構造函數
public ArrayList(int initialCapacity) {//可傳參
if (initialCapacity > 0) { //初始化容量大小大於0
this.elementData = new Object[initialCapacity];//new一個新的數組
} else if (initialCapacity == 0) { //參數爲0,那就用定義的空數組
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
public ArrayList() { //不傳參時調用這個構造方法
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/*
這裏參數表示:傳一個Collection進來爲了創建ArrayList對象時把Collection裏所有的內容放入ArrayList裏面去。比如傳進一個TreeSet的對象進來
*/
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();//得到這個對象的數組。方法定義在Collection接口裏
if ((size = elementData.length) != 0) {//如果當前數組長度不爲空
// c.toArray might (incorrectly) not return Object[] (see 6260652)
//c.toArray可能沒有返回一個Object數組,比如我們下面測試的傳的就是String類型的
if (elementData.getClass() != Object[].class)//判斷一下
elementData = Arrays.copyOf(elementData, size, Object[].class); //把elementData裏的內容當作Object複製出來,放入elementData裏
} else {/
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
2.trimToSize
public void trimToSize() {
modCount++;
if (size < elementData.length) { //如果elementData太長了,比如只放了一個值,size爲1,而數組長度爲10(默認),那就需要把多餘的切掉
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
3.ensureCapacity()
ensureCapacity()
是提供給用戶使用的方法,在 ArrayList
的實現中並沒有使用,他使用的是ensureCapacityInternal
public void ensureCapacity(int minCapacity) {
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) //minExpand:最小擴充容量,默認是10
? 0 //如果保存內容的數組 不是 默認的空數組(DEFAULTCAPACITY_EMPTY_ELEMENTDATA)(意思就是用戶指定了長度),那就先不要擴充(哪怕是用戶指定長度爲0也不要擴容,不要問爲什麼,用戶樂意呀)
: DEFAULT_CAPACITY; //如果是(意思就是通過無參的ArrayList創建的容器,用戶沒有指定長度,默認爲0。如果要添加一個元素則要擴容,擴容的大小就是默認的大小10),那就先擴充10個
//上面無參構造函數創建後,當元素第一次被加入時,擴容至默認容量 10,就是靠上面這句代碼
if (minCapacity > minExpand) { //如果最小容量大於最小擴充,則以用戶指定的爲準,否則還是10
ensureExplicitCapacity(minCapacity);
}
}
4.ensureCapacityInternal()、ensureExplicitCapacity()
add方法在添加元素之前會先調用ensureCapacityInternal
方法,主要是有兩個目的:1.如果沒初始化則進行初始化;2.校驗添加元素後是否需要擴容。
private void ensureCapacityInternal(int minCapacity) {
// 若 elementData == {},則取 minCapacity 爲 默認容量和參數 minCapacity 之間的最大值
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity= Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// 如果添加該元素後的大小超過數組大小,則進行擴容
if (minCapacity - elementData.length > 0)
grow(minCapacity);//擴容
}
5.grow() 擴容
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); //新的大小 = 老的大小+老的大小/2 如 oldCapacity = 10,則 newCapacity = 10 + (10 >> 1) = 10 + 5 = 15
if (newCapacity - minCapacity < 0) //若 newCapacity 依舊小於 minCapacity
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0) //若 newCapacity 大於最大存儲容量,則進行大容量分配
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity); //將elementData擴容爲newCapacity大小再賦值回去
}
把上面的代碼走一遍,方便理解。比如現在新創建一個對象,沒有放任何內容,調用ensureCapacity
傳的參數爲13, 這時elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA
,則minExpand = DEFAULT_CAPACITY
=10.然後13>10,調用ensureExplicitCapacity
,且把13傳入。因爲minCapacity - elementData.length
=13-10 > 0,所以執行grow,newCapacity
爲0,newCapacity - minCapacity
= 0-13 < 0,所以newCapacity
爲13.所以新數組elementData
大小變爲13
6.toArray()
/*
* 返回 ArrayList 的 Object 數組
* - 包含 ArrayList 的所有儲存元素
* - 對返回的該數組進行操作,不會影響該 ArrayList(相當於分配了一個新的數組)==>該操作是安全的
* - 元素存儲順序與 ArrayList 中的一致
*/
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
7.add()
public boolean add(E e) {
ensureCapacityInternal(size + 1); //第一件事就是確認長度,因爲如果往裏放一個值則可能導致數組溢出,所以先判斷一下。如果將導致溢出則自動擴容(原來大小的1.5倍)
elementData[size++] = e;
return true;
}
//相當於插入一個元素
public void add(int index, E element) {
rangeCheckForAdd(index); //檢查index是否合理
ensureCapacityInternal(size + 1); //確認長度
System.arraycopy(elementData, index, elementData, index + 1,
size - index);//表示在原來數組的基礎上將index位置上的值拷貝到index+1的位置上,從index開始依次往後拷貝size-index個
//eg:容量10,有效值有5個,index爲3,則要將3號位置的值拷貝到4號位置,將4號位置的值拷貝到5號位
elementData[index] = element;//將要加入的元素放在index處
size++;
}
8.remove()
/**
* 移除指定位置的元素
* index 之後的所有元素依次左移一位
* @param index 指定位置
* @return 被移除的元素
* @throws IndexOutOfBoundsException
*/
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1; //需要挪動多少個
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved); //從index+1的位置開始挪,向前挪一位,挪動numMoved個
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
學習參考:
https://blog.csdn.net/qq_33842627/article/details/88701461
https://www.cnblogs.com/gxl1995/p/7534171344218b3784f1beb90d621337.html