java中的數據結構源碼分析的系列文章:
ArrayList源碼分析
LinkedList源碼分析java
咱們知道,數據結構中有兩種存儲結構,分別是:順序存儲結構(線性表)、鏈式存儲結構(鏈表),在java中,對這兩種結構分別進行實現的類有:數組
本篇只對ArrayList的源碼進行分析,對於其餘類的源碼分析可經過本人博客列表進行查看。bash
在分析ArrayList以前,咱們先來看看集合的接口——List。數據結構
public interface List<E> extends Collection<E> {
boolean add(E e);
void add(int index, E element);
boolean remove(Object o);
E remove(int index);
E set(int index, E element);
E get(int index);
...
}複製代碼
在List這個接口中,提供了對集合進行操做的增、刪、改、查方法,咱們知道,ArrayList和LinkedList都實現了List接口,但它們的底層實現分別是線性表與鏈表,因此,對應的增、刪、改、查方法確定也不同,下面的分析也將從這幾個方法入手。dom
在ArrayList的源碼中,成員變量並很少,下面就截出其中幾個重要的變量進行說明。函數
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
...
private static final int DEFAULT_CAPACITY = 10;
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
transient Object[] elementData;
private int size;
...
}複製代碼
ArrayList的底層實現是數組,數組一定是有限長度的,ArrayList中默認的數組大小是10。源碼分析
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}複製代碼
這個構造函數是開發最經常使用的,能夠看到,它僅僅只是讓elementData等於一個空數組(DEFAULTCAPACITY_EMPTY_ELEMENTDATA)而已。post
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);
}
}複製代碼
這個構造函數能夠指定初始化數組的長度,當initialCapacity大於0時,爲elementData建立一個長度爲initialCapacity的Object數組;當initialCapacity等於0時,則讓elementData等於一個空數組(EMPTY_ELEMENTDATA)。ui
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}複製代碼
在前面已經提到了,size是一個成員變量,表示ArrayList中的元素個數。在這個方法中,先調用了ensureCapacityInternal()方法確保數組有足夠的容量,再對將元素添加到elementData數組中。下面就來看看ensureCapacityInternal()方法是如何確保數組有足夠的容量的。this
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}複製代碼
該方法結合ensureExplicitCapacity()方法,總的來講就是計算並擴大最小的容器體積。
這裏就用到了DEFAULTCAPACITY_EMPTY_ELEMENTDATA這個空數組,若是此時elementData與DEFAULTCAPACITY_EMPTY_ELEMENTDATA相等,說明開發者使用的是無參構造函數建立了集合,並且是添加第一個元素,此時的容器大小爲0。
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}複製代碼
當minCapacity - elementData.length > 0時,說明當前數組(容器)的空間不夠了,須要擴容,因此調用grow()方法。
modCount只是一個計數變量而已,源碼中有不少地方出現,無須理會。
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
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);
}複製代碼
grow()方法是用來給ArrayList集合進行擴容的,它計算出新的容器大小(即newCapacity),並確保了newCapacity不會比minCapacity小,最後調用Arrays.copyOf()建立一個新的數組並將數據拷貝到新數組中,最後讓elementData進行引用。
oldCapacity >> 1 等價於 oldCapacity / 2,也就是說ArrayList默認的擴容大小是當前數組大小的一半。
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}複製代碼
通過對add(E e)方法進行分析,這個增長方法就很容易理解了,它先確保容器有足夠大的空間,沒有就擴容,而後將elementData數組中從index位置開始的全部元素日後"移動"1位,再對數組的index位置進行元素賦值,最後將記錄集合中元素個數的size變量加1。
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);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}複製代碼
numMoved表示在執行刪除操做時數組須要移動的元素個數,將elementData數組中從index後一位開始的全部元素(即numMoved個元素)往前"移動"1位(這樣一移動,index位置的元素會被後面的元素覆蓋,間接起到了刪除元素的做用),而後把最後的那個元素置空,同時將size變量減1。
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++;
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
}複製代碼
該方法的操做與remove(int index)基本一致,這裏就再也不說明了。(fastRemove()方法的代碼不是能夠複用麼。。。)
ArrayList的修改和獲取元素的方法至關簡單,就是對elementData數組進行簡單操做罷了,這裏就列出源碼看看就行了。
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
E elementData(int index) {
return (E) elementData[index];
}複製代碼
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}複製代碼