1.回顧
在上一篇 我們介紹了java集合的整體架構,為了複習方便請重新看下圖
Colletion 有List Set等子介面 而每個子介面又有具體的實現類,本文要講的ArrayList就是List的一種實現。List可儲存有序可重複的元素,那麼可知ArrayList也是。相反Set卻是無序不可重複的。
2.結構
那麼我們開始重點看ArrayList,通過idea可得下圖
由上圖知
實現RandomAccess介面:可以通過下標序號快速訪問
實現了Cloneable,能被克隆
實現了Serializable,支援序列化
再看類中方法如下
可見ArrayList除了實現類Collection介面方法外 還多了許多過載多方法 例如
void add(int index, E element)
boolean addAll(int index, Collection<? extends E> c)
E get(int index)
int indexOf(Object o)
int lastIndexOf(Object o)
E remove(int index)
E set(int index, E element)
List<E> subList(int fromIndex, int toIndex)
複製程式碼
上面的方法 顧名思義都可以猜出來 由於ArrayList是有序可重複的,其中維護了一個索引index 固可以以指定索引index來進行更多的操作,例如獲取指定索引位置的元素,刪除指定索引位置的元素,擷取某個索引範圍的所有元素等等
3.原始碼分析
一.成員變數
private static final long serialVersionUID = 8683452581122892189L;
private static final int DEFAULT_CAPACITY = 10;
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
transient Object[] elementData; // non-private to simplify nested class access
private int size;
複製程式碼
- DEFAULT_CAPACITY預設容量是10
- EMPTY_ELEMENTDATA 陣列
- DEFAULTCAPACITY_EMPTY_ELEMENTDATA
- elementData 存放元素
- size 元素個數
二.建構函式
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() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
複製程式碼
有三個建構函式 分析程式碼可知可初始化時傳遞一個int值指定容量,也可以不傳預設10的容量 還可以傳遞一個指定Collection 來初始化 其中第三個指定Collection來初始化較長,無非就是先把集合轉化成陣列 然後把元素複製到list中去來完成初始化
三.常用方法
新增
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
複製程式碼
其中呼叫 ensureCapacityInternal 方法
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
複製程式碼
再呼叫 ensureExplicitCapacity 方法
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
複製程式碼
又呼叫 grow 方法
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);
}
複製程式碼
總結以上便是新增前需要確定容量,噹噹前陣列容量不夠時就需要擴容,然後調Arrays.copyOf()建立一個新的陣列並將資料拷貝到新陣列中,且把引用賦值給elementData
刪除remove
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;
}
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;
}
複製程式碼
兩種過載方法 先檢查範圍 然後拿到被刪的元素 然後把被刪元素後到元素往前移動 複製到目標陣列 然後返回被刪元素
修改set
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
複製程式碼
這段程式碼比較簡單 範圍檢查和重新賦值
獲取get
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
複製程式碼
這個估計都不用我說把
4.總結
通過結構原始碼都分析我們知道,ArrayList 底層就是通過陣列來實現,新增的時候檢查容量不夠就擴容,然後賦值 刪除時則把被刪元素後往前移動 更新則比較簡潔明白。