按照從構(gòu)造方法->常用API(增、刪、改、查)的順序來閱讀源碼,并做簡要分析。
一. 定義
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
介紹
- 繼承了AbstractList,實現(xiàn)了List
它是一個數(shù)組隊列,提供了相關(guān)的添加、刪除、修改、遍歷等功能 - 實現(xiàn)了RandmoAccess接口
即提供了隨機(jī)訪問功能 - 實現(xiàn)了Cloneable接口
即覆蓋了函數(shù)clone(),能被克隆 - 實現(xiàn)java.io.Serializable接口
支持序列化,能通過序列化去傳輸
優(yōu)勢
- 比數(shù)組的優(yōu)勢
ArrayList 是一個數(shù)組隊列,相當(dāng)于動態(tài)數(shù)組。與Java中的數(shù)組相比,它的容量能動態(tài)增長 - 比Vector的優(yōu)勢
和Vector不同,ArrayList中的操作不是線程安全的,但是開銷小,在單線程環(huán)境性能優(yōu)于Vector
二 .屬性
底層用數(shù)組來保存數(shù)據(jù)
private transient Object[] elementData;
private int size;
- size :當(dāng)前數(shù)組長度
- elementData:存放數(shù)組的對象的數(shù)組
- modCount:ArrayList集合的修改次數(shù)
基于數(shù)組實現(xiàn),保存元素的數(shù)組使用 transient 修飾,該關(guān)鍵字聲明數(shù)組默認(rèn)不會被序列化。這是 ArrayList 具有動態(tài)擴(kuò)容特性,因此保存元素的數(shù)組不一定都會被使用,那么就沒必要全部進(jìn)行序列化。ArrayList 重寫了 writeObject() 和 readObject() 來控制只序列化數(shù)組中有元素填充那部分內(nèi)容。
三. 構(gòu)造函數(shù)
1)無參構(gòu)造函數(shù)--ArrayList()
Constructs an empty list with an initial capacity of ten.
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
Any empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA will be expanded to DEFAULT_CAPACITY when the first element is added.
private static final int DEFAULT_CAPACITY = 10;
此時我們創(chuàng)建的ArrayList對象中的elementData中的長度是1,size是0,當(dāng)進(jìn)行第一次add的時候,elementData將會變成默認(rèn)的長度:10.(具體過程看下文的add())
2)帶容量大小的構(gòu)造函數(shù)--ArrayList(int initialCapacity)
傳入?yún)?shù)如果是大于等于0,則使用用戶的參數(shù)初始化,如果用戶傳入的參數(shù)小于0,則拋出異常
Constructs an empty list with the specified initial capacity.
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);
}
}
3)帶Collection對象的構(gòu)造函數(shù)--ArrayList(Collection<? extends E> c)
構(gòu)造一個包含指定元素的list,這些元素的是按照Collection的迭代器返回的順序排列的
- 將collection對象轉(zhuǎn)換成數(shù)組,然后將數(shù)組的地址的賦給elementData
- 更新size的值,同時判斷size的大小
2.1 如果是size等于0,直接將空對象EMPTY_ELEMENTDATA的地址賦給elementData
2.2 如果size的值大于0,則執(zhí)行Arrays.copy方法,把collection對象的內(nèi)容copy到elementData中
Constructs a list containing the elements of the specified collection, in the order they are returned by the collection's iterator.
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
//因為size代表的是集合元素數(shù)量,所以通過別的集合來構(gòu)造ArrayList時,要給size賦值
if ((size = elementData.length) != 0) {
//這里是當(dāng)c.toArray出錯,沒有返回Object[]時
//利用Arrays.copyOf 來復(fù)制集合c中的元素到elementData數(shù)組中
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
private static final Object[] EMPTY_ELEMENTDATA = {};
Arrays.copyOf()方法
public static <T> T[] copyOf(T[] original, int newLength) {
return (T[]) copyOf(original, newLength, original.getClass());
}
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
可以看到,他是在方法內(nèi)部,新建了一個相同類型的數(shù)組,然后調(diào)用系統(tǒng)方法
System.arraycopy(original, 0, copy, 0,Math.min(original.length, newLength));
original - 源數(shù)組。
0 - 源數(shù)組中的起始位置。
copy - 目標(biāo)數(shù)組。
0 - 目標(biāo)數(shù)據(jù)中的起始位置。
Math.min(original.length, newLength) - 要復(fù)制的數(shù)組元素的數(shù)量。
四. 增加---add addAll
添加元素時使用 ensureCapacity() 方法來保證容量足夠,如果不夠時,需要使用 grow() 方法進(jìn)行擴(kuò)容,使得新容量為舊容量的 1.5 倍oldCapacity + (oldCapacity >> 1)
擴(kuò)容操作需要把原數(shù)組整個復(fù)制到新數(shù)組中,因此最好在創(chuàng)建 ArrayList 對象時就指定大概的容量大小,減少擴(kuò)容操作的次數(shù)。
添加一個元素(在末尾)--add(E e)
add(E e)
Appends the specified element to the end of this list.
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;//在數(shù)組末尾追加一個元素,并修改size
return true;
}
ensureCapacityInternal(int minCapacity)
- 確保添加的元素有地方存儲
- 當(dāng)?shù)谝淮翁砑釉氐臅r候this.size+1 的值是1,所以第一次添加的時候會將當(dāng)前elementData數(shù)組的長度變?yōu)?0
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
calculateCapacity(Object[] elementData, int minCapacity)
private static int calculateCapacity(Object[] elementData, int minCapacity) {
//利用 == 可以判斷數(shù)組是否是用默認(rèn)構(gòu)造函數(shù)初始化的
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private static final int DEFAULT_CAPACITY = 10;
ensureExplicitCapacity()
經(jīng)過前面的一頓操作,minCapacity 是
- DEFAULT_CAPACITY(10)
當(dāng)?shù)谝淮翁砑訒r - size + 1
當(dāng)不是第一次添加時
private void ensureExplicitCapacity(int minCapacity) {
modCount++; //如果確定要擴(kuò)容,會修改modCount
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
grow()
Increases the capacity to ensure that it can hold at least the number of elements specified by the minimum capacity argument
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); // 舊容量的1.5倍
//如果還不夠 ,那么就用 能容納的最小的數(shù)量。(add后的容量)
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
在指定位置添加一個元素--add(int index, E element)
Inserts the specified element at the specified position in this list.
- 確保有足夠的容量之后
- 使用System.arraycopy 將需要插入的位置(index)后面的元素統(tǒng)統(tǒng)往后移動一位
- 將新的數(shù)據(jù)內(nèi)容存放到數(shù)組的指定位置(index)上
public void add(int index, E element) {
rangeCheckForAdd(index);
// 進(jìn)行擴(kuò)容檢查
ensureCapacityInternal(size + 1); // Increments modCount!!
// 對數(shù)組進(jìn)行復(fù)制處理,目的就是空出index的位置插入element,并將index后的元素位移一個位置
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
// 將指定的index位置賦值為element
elementData[index] = element;
size++;
}
關(guān)于arraycopy()
public static void (Object src,int srcPos,
Object dest,int destPos,
int length)
| 參數(shù) | 意義 |
|---|---|
| src | 源數(shù)組 |
| srcPos | 源數(shù)組要復(fù)制的起始位置 |
| dest | 目的數(shù)組 |
| destPos | 目的數(shù)組放置的起始位置 |
| length | 復(fù)制的長度 |
- src和dest都必須是同類型或者可以進(jìn)行轉(zhuǎn)換類型的數(shù)組
- 可以實現(xiàn)自己到自己復(fù)制
其中rangeCheckForAdd是為了防止越界:
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
增加一個集合--addAll(Collection<? extends E> c)
Appends all of the elements in the specified collection to the end of this list, in the order that they are returned by the specified collection's Iterator.
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();//將集合轉(zhuǎn)換為數(shù)組
int numNew = a.length;
//擴(kuò)容檢查
ensureCapacityInternal(size + numNew); // Increments modCount
//將c添加至list的數(shù)據(jù)尾部
System.arraycopy(a, 0, elementData, size, numNew);
//更新當(dāng)前容器大小
size += numNew;
return numNew != 0;
}
在指定位置,增加一個集合元素--addAll(int index, Collection<? extends E> c)
Inserts all of the elements in the specified collection into this list, starting at the specified position.
public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index);
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
//計算需要移動的長度(index之后的元素個數(shù))
int numMoved = size - index;
//將數(shù)組index后的元素向右移動numNum個位置,即空出第index到index+numNum的位置
if (numMoved > 0)
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);
//將要插入的集合元素復(fù)制到數(shù)組空出的位置中
System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;
return numNew != 0;
}
關(guān)于增 的總結(jié):
add、addAll。
先判斷是否越界,是否需要擴(kuò)容。
如果擴(kuò)容, 就復(fù)制數(shù)組。
然后設(shè)置對應(yīng)下標(biāo)元素值。
值得注意的是:
1 如果需要擴(kuò)容的話,默認(rèn)擴(kuò)容一半。如果擴(kuò)容一半不夠,就用目標(biāo)的size作為擴(kuò)容后的容量。
2 在擴(kuò)容成功后,會修改modCount
五. 刪除---remove
根據(jù)索引位置刪除元素--remove(int index)
Removes the element at the specified position in this list.
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);// 供返回
int numMoved = size - index - 1;// 計算數(shù)組要復(fù)制的數(shù)量
// 數(shù)組復(fù)制,就是將index之后的元素往前移動一個位置
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
// 因為刪除了一個元素,然后index后面的元素都向前移動了,所以最后一個就沒用了
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
根據(jù)元素內(nèi)容刪除,只刪除匹配的第一個--remove(Object o)
Removes the first occurrence of the specified element from this list, if it is present. If the list does not contain the element, it is unchanged.
循環(huán)遍歷所有對象,得到對象所在索引位置,然后調(diào)用fastRemove方法,執(zhí)行remove操作
- null值要用==比較
public boolean remove(Object 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;
}
fastRemove()
Private remove method that skips bounds checking and does not return the value removed.
定位到需要remove的元素索引,先將index后面的元素往前面移動一位(調(diào)用System.arraycooy實現(xiàn)),然后將最后一個元素置空。
private void fastRemove(int index) {
modCount++;
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
}
數(shù)組越界檢查
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private String outOfBoundsMsg(int index) {
return "Index: "+index+", Size: "+size;
}
六. 查找---get
查找指定位置上的元素
Returns the element at the specified position in this list.
public E get( int index) {
RangeCheck(index);
return (E) elementData [index];
}
七. 更新---set
將指定位置的元素更新為新元素
Replaces the element at the specified position in this list with the specified element.
- 注意返回值是oldValue
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);// 記錄要更新位置的元素,供返回使用
elementData[index] = element;
return oldValue;
}
八. 判斷
包含--contains(Object o)
調(diào)用indexOf方法,遍歷數(shù)組中的每一個元素作對比,如果找到對于的元素,則返回true,沒有找到則返回false
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
空--isEmpty()
public boolean isEmpty() {
return size == 0;
}
九. ArrayList遍歷方式
ArrayList支持3種遍歷方式
- 通過迭代器遍歷。即通過Iterator去遍歷。
Integer value = null;
Iterator iter = list.iterator();
while (iter.hasNext()) {
value = (Integer)iter.next();
}
- 隨機(jī)訪問,通過索引值去遍歷。
由于ArrayList實現(xiàn)了RandomAccess接口,它支持通過索引值去隨機(jī)訪問元素。
Integer value = null;
int size = list.size();
for (int i=0; i<size; i++) {
value = (Integer)list.get(i);
}
- for循環(huán)遍歷。如下:
Integer value = null;
for (Integer integ:list) {
value = integ;
}
十. ArrayList和 Vector 的區(qū)別
- 同步
Vector 和 ArrayList 幾乎是完全相同的,唯一的區(qū)別在于 Vector 是同步的,因此開銷就比 ArrayList 要大,訪問速度更慢。最好使用 ArrayList 而不是 Vector,因為同步操作完全可以由程序員自己來控制; - 擴(kuò)容
Vector 每次擴(kuò)容請求其大小的 2 倍空間,而 ArrayList 是 1.5 倍。
Vector 的grow()擴(kuò)容部分
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
十一. 小回顧
- 添加元素時可能要擴(kuò)容(所以最好預(yù)判一下),刪除元素時不會減少容量(若希望減少容量,trimToSize()),刪除元素時,將刪除掉的位置元素置為null,下次gc就會回收這些元素所占的內(nèi)存空間
- add(int index, E element):添加元素到數(shù)組中指定位置的時候,需要將該位置及其后邊所有的元素都整塊向后復(fù)制一位
- get(int index):獲取指定位置上的元素時,可以通過索引直接獲取(O(1))
- remove(Object o)需要遍歷數(shù)組
- remove(int index)不需要遍歷數(shù)組,只需判斷index是否符合條件即可,效率比remove(Object o)高
- contains(E)需要遍歷數(shù)組
參考文章
Java集合干貨系列-(一)ArrayList源碼解析
Java官方文檔
ArrayList源碼解析(JDK8)