Java集合之ArrayList源碼剖析(jdk1.8)
1、簡介
ArrayList是一種以數組實現的List,與數組相比,它具有動態擴展的能力,因此也可稱之爲動態數組。
查詢快,增刪慢,線程不安全。
繼承圖譜如下:
ArrayList實現了List, RandomAccess, Cloneable, java.io.Serializable等接口。
ArrayList實現了List,提供了基礎的添加、刪除、遍歷等操作。
ArrayList實現了RandomAccess,提供了隨機訪問的能力。
ArrayList實現了Cloneable,可以被克隆。
ArrayList實現了Serializable,可以被序列化。
2、源碼分析
1、屬性
//用於序列化的id
private static final long serialVersionUID = 8683452581122892189L;
//默認構造方法的容量
private static final int DEFAULT_CAPACITY = 10;
//空數組,構造方法中參數爲0時使用--->new ArrayList(0)
private static final Object[] EMPTY_ELEMENTDATA = {};
//空數組,默認構造方法使用--->new ArrayList()
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//臨時存放元素的數組,使用transient修飾,不會被序列化
transient Object[] elementData;
//元素實際個數
private int size;
//數組能分配的最大內存,超出會OutOfMemoryError
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
2、構造方法
ArrayList有三個構造方法。
//構造方法一,傳入容量
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);
}
}
//構造方法二,不傳入初始容量
public ArrayList() {
//當添加第一個元素時,擴容到默認大小10
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//構造方法三,傳入一個集合,將其轉化爲ArrayList
public ArrayList(Collection<? extends E> c) {
//集合轉數組
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// 檢查c.toArray()返回的是不是Object[]類型,如果不是,重新拷貝成Object[].class類型
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// 如果c的空集合,則初始化爲空數組EMPTY_ELEMENTDATA
this.elementData = EMPTY_ELEMENTDATA;
}
}
3、簡單方法
這部分方法有的是從高層繼承下來的,有的是對高層方法的實現,較爲簡單,部分如下:
public int size() {
return size;
}
public boolean isEmpty() {
return size == 0;
}
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
4、核心方法
這部分方法有些是對抽象List的實現,也有部分獨屬於ArrayList.
add(E e)方法
在ArrayList後面添加一個元素
public boolean add(E e) {
// 檢查是否需要擴容
ensureCapacityInternal(size + 1);
// 把元素插入到最後一位
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
//計算容量
private static int calculateCapacity(Object[] elementData, int minCapacity) {
// 如果是空數組DEFAULTCAPACITY_EMPTY_ELEMENTDATA,就初始化爲默認大小10
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
if (minCapacity - elementData.length > 0)
// 擴容
grow(minCapacity);
}
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
// 新容量爲舊容量的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 如果新容量發現比需要的容量還小,則以需要的容量爲準
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 如果新容量已經超過最大容量了,則使用最大容量
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 以新容量拷貝出來一個新數組
elementData = Arrays.copyOf(elementData, newCapacity);
}
add(int index, E element)方法
添加元素到指定位置,平均時間複雜度爲O(n)。
public void add(int index, E element) {
//檢查是否越界
rangeCheckForAdd(index);
//檢查是否需要擴容
ensureCapacityInternal(size + 1);
//將index及其後的元素向後移動一位,空出index的位置
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
//空出的位置存放加入的元素
elementData[index] = element;
//數量加一
size++;
}
addAll(Collection<? extends E> c)方法
向ArrayList中加入集合,求並集
public boolean addAll(Collection<? extends E> c) {
//集合轉爲數組
Object[] a = c.toArray();
int numNew = a.length;
//檢查擴容
ensureCapacityInternal(size + numNew);
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
addAll(int index, Collection<? extends E> c)
從指定位置加入集合。
public boolean addAll(int index, Collection<? extends E> c) {
//檢查越界
rangeCheckForAdd(index);
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew);
int numMoved = size - index;
if (numMoved > 0)
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);
System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;
return numNew != 0;
}
get(int index)方法
通過索引獲取元素,複雜度爲o(1)
public E get(int index) {
// 檢查是否越界
rangeCheck(index);
// 返回數組index位置的元素
return elementData(index);
}
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
E elementData(int index) {
return (E) elementData[index];
}
remove(int index)方法
通過索引刪除元素,複雜度爲o(1)
public E remove(int index) {
// 檢查是否越界
rangeCheck(index);
modCount++;
// 獲取index位置的元素
E oldValue = elementData(index);
// 如果index不是最後一位,則將index之後的元素往前挪一位
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index, numMoved);
// 將最後一個元素刪除,幫助GC
elementData[--size] = null; // clear to let GC do its work
// 返回舊值
return oldValue;
}
remove(Object o)方法
刪除指定元素值的元素,時間複雜度爲O(n)。
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;
}
private void fastRemove(int index) {
modCount++;
// 如果index不是最後一位,則將index之後的元素往前挪一位
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index, numMoved);
// 將最後一個元素刪除,幫助GC
elementData[--size] = null; // clear to let GC do its work
}
removeAll(Collection<?> c)方法
批量刪除,也可以單方向求差集,複雜度o(n).
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);
//刪除所有在集合c中的元素
return batchRemove(c, false);
}
//批量刪除,false代表在集合中的元素統統刪除
private boolean batchRemove(Collection<?> c, boolean complement) {
final Object[] elementData = this.elementData;
int r = 0, w = 0;
boolean modified = false;
try {
for (; r < size; r++)
if (c.contains(elementData[r]) == complement)
elementData[w++] = elementData[r];
} finally {
// Preserve behavioral compatibility with AbstractCollection,
// even if c.contains() throws.
if (r != size) {
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
if (w != size) {
// clear to let GC do its work
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
size = w;
modified = true;
}
}
return modified;
}
retainAll(Collection<?> c)方法
求交集,複雜度o(n)
public boolean retainAll(Collection<?> c) {
Objects.requireNonNull(c);
//此處爲true,表明刪除所有不在c中的元素
return batchRemove(c, true);
}
set(int index, E element) 方法
更新元素,複雜度o(1)
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
迭代器方法
ArrayList中的迭代器有兩種。
public Iterator<E> iterator() {
return new Itr();
}
public ListIterator<E> listIterator() {
return new ListItr(0);
}
public ListIterator<E> listIterator(int index) {
if (index < 0 || index > size)
throw new IndexOutOfBoundsException("Index: "+index);
return new ListItr(index);
}
//迭代器
private class Itr implements Iterator<E> {
//可以向後遍歷
}
//list迭代器
private class ListItr extends Itr implements ListIterator<E> {
//既可以向前也可以向後遍歷
}
3、總結
(1)ArrayList內部使用數組存儲元素,當數組長度不夠時進行擴容,每次加一半的空間,ArrayList不會進行縮容;
(2)ArrayList支持隨機訪問,通過索引訪問元素極快,時間複雜度爲O(1);
(3)ArrayList支持求並集,調用addAll(Collection<? extends E> c)方法即可;
(4)ArrayList支持求交集,調用retainAll(Collection<? extends E> c)方法即可;
(5)ArrayList支持求單向差集,調用removeAll(Collection<? extends E> c)方法即可;
(6)ArrayList有兩種迭代器,支持向前向後遍歷。
(7)ArrayList底層基於數組,所以除了迭代器,也可以使用循環遍歷。
(8)ArrayList查詢快,增刪慢,線程不安全。