ArrayList是咱們很是經常使用的一個集合,那麼ArrayList是如何實現呢?java
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("Hello");
if (arrayList.contains("Hello")) {
System.out.println("[Hello] is in ArrayList!");
}
arrayList.remove("Hello");
複製代碼
public ArrayList() {
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//存儲數據的集合
transient Object[] elementData; // non-private to simplify nested class access
//....
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
複製代碼
從構造函數能夠看出,如今咱們的集合是一個空集合,真正存儲數據的數組也是一個長度爲0的數組。數組
public boolean add(E e) {
modCount++;
add(e, elementData, size);
return true;
}
private void add(E e, Object[] elementData, int s) {
//存儲數據的數組已經所有被使用
if (s == elementData.length)
//擴張數組
elementData = grow();
elementData[s] = e;
size = s + 1;
}
複製代碼
一個參數的add方法調用了private的add方法,在該方法中真正完成了向集合中添加元素。在添加元素的時候,若是size==存儲數據的數組的長度,那麼就代表數組已經存滿了,這時候就須要將本來的數組進行擴張,如何擴張數組是經過 grow
方法來實現的。函數
private Object[] grow() {
return grow(size + 1);
}
private Object[] grow(int minCapacity) {
//複製當前數組到一個新的數組中(新的數組長度已經擴張過)
return elementData = Arrays.copyOf(elementData,
newCapacity(minCapacity));
}
//計算新的擴張長度
private int newCapacity(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//計算新的容量
//新容量= 數組的長度+數組的長度向右位移1(例如:10>>1 = 5, 11>>1 = 5)
//這種新容量的計算方式代表:當集合的長度越大時,集合每一次的擴張的幅度就會愈來愈大
int newCapacity = oldCapacity + (oldCapacity >> 1);
//當新容量小於最小容量時,以最小容量爲基準
if (newCapacity - minCapacity <= 0) {
//當數組中沒有元素的時候,會執行這個分支
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
//返回最小容量和默認容量中較大的一個
return Math.max(DEFAULT_CAPACITY, minCapacity);
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return minCapacity;
}
return (newCapacity - MAX_ARRAY_SIZE <= 0)
? newCapacity
: hugeCapacity(minCapacity);//極端狀況,能夠不考慮(幾乎不會走到這個分支)
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE)
? Integer.MAX_VALUE
: MAX_ARRAY_SIZE;
}
複製代碼
Grow 方法是實現ArrayList長度可變的核心,其實現思想是源碼分析
當數組存放滿了的時候,就擴張數組(當集合中的元素的數量越多的時候,擴張的幅度就越大。) 而擴張數組是經過,創建新的數組(長度等於擴張後的長度),而後將舊的數組中的元素填充到新數組中,這種方式來實現的。this
Contains也是ArrayList集合很是經常使用的一個方法,用來判斷集合中是否包含指定的元素。spa
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
public int indexOf(Object o) {
return indexOfRange(o, 0, size);
}
int indexOfRange(Object o, int start, int end) {
Object[] es = elementData;
if (o == null) {
for (int i = start; i < end; i++) {
if (es[i] == null) {
return i;
}
}
} else {
for (int i = start; i < end; i++) {
if (o.equals(es[i])) {
return i;
}
}
}
return -1;
}
複製代碼
contains方法最終調用了調用了indexOfRange方法,indexOfRange作的工做是,在集合指定的範圍內判斷是否包含指定的元素,若是包含就返回下標,不然就返回-1.code
public boolean remove(Object o) {
final Object[] es = elementData;
final int size = this.size;
int i = 0;
//搜尋指定元素
found: {
if (o == null) {
for (; i < size; i++)
if (es[i] == null)
break found;
} else {
for (; i < size; i++)
if (o.equals(es[i]))
break found;
}
return false;
}
//移除元素
fastRemove(es, i);
return true;
}
private void fastRemove(Object[] es, int i) {
modCount++;
final int newSize;
//若是不是在數組的末尾移除元素
if ((newSize = size - 1) > i)
//將i以後全部元素向前移動一個下標
System.arraycopy(es, i + 1, es, i, newSize - i);
es[size = newSize] = null;
}
複製代碼
remove方法的邏輯很清晰:ci
ArrayList經過數組實現了可變集合,可是咱們從源碼中能夠看出來,若是要增長刪除元素的話,是很是消耗資源和時間的(由於在頻繁的操做數組),可是訪問的時候是特別的快的(直接經過下標訪問便可)。element
能夠得出結論: ArrayList不適合頻繁的增長刪除,可是適合查詢。資源