哈嘍,大家好,今天來(lái)聊一聊我們?cè)贏ndroid開(kāi)發(fā)中經(jīng)常用的到一個(gè)類,ArrayList
ArrayList是以數(shù)組為底層實(shí)現(xiàn)的,并且可以動(dòng)態(tài)的加減容量。但是要注意它不是線程安全的。
在分析源碼之前,我們先來(lái)了解兩個(gè)相關(guān)的知識(shí)點(diǎn),這樣更加有助于我們理解源碼。
Arrays.copyof
這個(gè)方法是用于復(fù)制數(shù)組的方法
public static <T> T[] copyOf(T[] original, int newLength) {
return (T[]) copyOf(original, newLength, original.getClass());
}
我們可以看到這里是傳入了兩個(gè)參數(shù),第一個(gè)參數(shù)是要復(fù)制的數(shù)組,第二個(gè)參數(shù)是復(fù)制后數(shù)組的長(zhǎng)度,如果傳入長(zhǎng)度和original的長(zhǎng)度一樣,那么就會(huì)復(fù)制original里面所有的數(shù)據(jù)。如果比original的長(zhǎng)度短,那么久復(fù)制到相應(yīng)長(zhǎng)度即可,如果比original的長(zhǎng)度還要長(zhǎng),那么就會(huì)用null或者0來(lái)填充。
System arraycopy
我們繼續(xù)跟進(jìn)上面Arrays.copyof的源碼我們會(huì)發(fā)現(xiàn):
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
@SuppressWarnings("unchecked")
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;
}
@FastNative
public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
我們發(fā)現(xiàn)Arrays.copyof最終調(diào)用的就是System.arraycopy方法,這是一個(gè)native方法,參數(shù)解釋如下:
- src:要復(fù)制的數(shù)組。
- srcPos:復(fù)制的起點(diǎn)。
- dest:復(fù)制的目標(biāo)數(shù)組。
- destPos:目標(biāo)數(shù)組復(fù)制的起點(diǎn)
- length:要復(fù)制的值的個(gè)數(shù)
要注意的是這里復(fù)制過(guò)后修改復(fù)制后的新數(shù)組并不會(huì)影響原來(lái)的數(shù)組的值。
接下來(lái)我們進(jìn)入正題,開(kāi)始聊聊ArrayList。
我們先來(lái)看看ArrayList實(shí)現(xiàn)了哪些接口
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
- ArrayList實(shí)現(xiàn)了List這個(gè)接口,在List里我們可以看到有add,get,set等方法。
- RandomAccess是一個(gè)標(biāo)志接口,實(shí)現(xiàn)RandomAccess這個(gè)接口后ArrayList支持快速隨機(jī)訪問(wèn)。
- 實(shí)現(xiàn)Serializable后ArrayList支持序列化。
接下來(lái)我們來(lái)看看類里面的方法:
ArrayList(int initialCapacity)
private static final Object[] EMPTY_ELEMENTDATA = {};
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);
}
}
這個(gè)構(gòu)造函數(shù)傳入的initialCapacity為初始化ArrayList的長(zhǎng)度。如果長(zhǎng)度大于0,則new一個(gè)長(zhǎng)度為initialCapacity的數(shù)組并賦值給elementData。如果長(zhǎng)度為0,這直接將默認(rèn)空數(shù)組賦值給elementData。如果長(zhǎng)度小于0,則報(bào)錯(cuò)。
ArrayList()
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
這個(gè)無(wú)參構(gòu)造函數(shù)直接是將一個(gè)空數(shù)組賦值給elementData。
ArrayList(Collection<? extends E> c)
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
this.elementData = EMPTY_ELEMENTDATA;
}
}
這個(gè)構(gòu)造函數(shù)傳入了一個(gè)集合,先將傳入的參數(shù)轉(zhuǎn)換為數(shù)組并賦值給elementData。再進(jìn)行elementData數(shù)組的長(zhǎng)度判斷,如果長(zhǎng)度大于0,將elementData數(shù)組轉(zhuǎn)換為一個(gè)Object的數(shù)組。如果elementData長(zhǎng)度為0,則直接賦值一個(gè)空數(shù)組。
indexOf(Object o)
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;
}
這里先判斷傳入?yún)?shù)是否為null,因?yàn)锳rrayList是可以添加null的,所以我們要先判斷一下,利用for循環(huán)來(lái)一個(gè)一個(gè)對(duì)比,如果為null,則返回下標(biāo)。如果不為null,也是在for循環(huán)中做值的對(duì)比,注意這里用的equals來(lái)對(duì)比的值而不是用的==,找到后返回下標(biāo),如果沒(méi)有則返回-1。
get(int index)
public E get(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
return (E) elementData[index];
}
get方法是獲取對(duì)應(yīng)下標(biāo)的值,這里先判斷傳入的參數(shù)是否越界,如果沒(méi)有直接從數(shù)組中取出對(duì)應(yīng)的值,并返回。
set(int index, E element)
public E set(int index, E element) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
E oldValue = (E) elementData[index];
elementData[index] = element;
return oldValue;
}
set方法是修改對(duì)應(yīng)下標(biāo)的值,這里先判斷傳入的index是否越界,如果沒(méi)有我們獲取現(xiàn)在下標(biāo)對(duì)應(yīng)的值,然后賦上新的值,并將原來(lái)的值返回。
add(E e)
public boolean add(E e) {
//這里先判斷是否需要增加數(shù)組的容量
ensureCapacityInternal(size + 1); // Increments modCount!!
//賦值到最后一位。
elementData[size++] = e;
return true;
}
private static final int DEFAULT_CAPACITY = 10;
private void ensureCapacityInternal(int minCapacity) {
//如果數(shù)組在初始化的時(shí)候沒(méi)有傳入數(shù)組大小,我們先設(shè)置一個(gè)最小值。
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
//操作次數(shù)加1
modCount++;
//如果數(shù)組容量不夠,需要增容。
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {
//獲取現(xiàn)在數(shù)組的大小。
int oldCapacity = elementData.length;
//獲取現(xiàn)在大小1.5倍的值。
int newCapacity = oldCapacity + (oldCapacity >> 1);
//如果還是不夠大,那么就直接讓數(shù)組大小為minCapacity。
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//如果超過(guò)了規(guī)定的最大值,將用傳入的最小值來(lái)重新計(jì)算數(shù)組的大小。
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
//最后賦值所有值到新的數(shù)組。
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
//如果符合條件的最小值大于規(guī)定的最大值,那么將值修改為Integer.MAX_VALUE,否則為規(guī)定的最大值。
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
關(guān)鍵解釋已經(jīng)注釋在代碼中。
add(int index, E element)
public void add(int index, E element) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
這個(gè)add方法和上一個(gè)add方法大致相同,在ensureCapacityInternal(size + 1)方法后調(diào)用System.arraycopy方法將數(shù)組從index下標(biāo)位置全部向后移動(dòng)一位,然后直接將index下標(biāo)位置賦上新傳進(jìn)來(lái)的值。
remove(int index)
public E remove(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
modCount++;
E oldValue = (E) 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;
}
在remove(int index)方法中我們先判斷傳入的index是否越界,如果沒(méi)有我們將index+1位置開(kāi)始的所有值向前移動(dòng)一位,這樣就覆蓋了index的值,然后將最后多出來(lái)的一位賦值為null,方便GC處理。
remove(Object o)
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(Object o)方法我們需要先判斷傳入的Object是否為null,然后找到Object值的下標(biāo),因?yàn)檫@里已經(jīng)找到了下標(biāo),所以肯定沒(méi)有越界,然后直接調(diào)用fastRemove方法來(lái)刪除。fastRemove方法和上面的remove方法類似,這里也不再解釋了。
addAll(Collection<? extends E> c)
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
這里先通過(guò)ensureCapacityInternal(size + numNew)方法來(lái)判斷是否需要增容,然后直接調(diào)用System.arraycopy方法將新的數(shù)組賦值到elementData中。
addAll(int index, Collection<? extends E> c)
public boolean addAll(int index, Collection<? extends E> c) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
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;
}
addAll(int index, Collection<? extends E> c)方法先判斷了傳入的index是否越界,如果沒(méi)有越界我們繼續(xù)判斷數(shù)組是否需要增容。然后這里有一個(gè)判斷,如果numMoved 的值大于0,就說(shuō)明index在數(shù)組的內(nèi),如果小于等于0,說(shuō)明index在數(shù)組最后一位或者在數(shù)組外,根據(jù)不同,分別調(diào)用System.arraycopy來(lái)進(jìn)行賦值。
removeAll(Collection<?> c)
public boolean removeAll(Collection<?> c) {
//先判斷c是否為null。
Objects.requireNonNull(c);
return batchRemove(c, false);
}
private boolean batchRemove(Collection<?> c, boolean complement) {
final Object[] elementData = this.elementData;
int r = 0, w = 0;
boolean modified = false;
try {
//這里傳入的complement為false,那么for中就是將不刪除的是有值全部放到最前面去。
for (; r < size; r++)
if (c.contains(elementData[r]) == complement)
elementData[w++] = elementData[r];
} finally {
//如果r不等于size,那么說(shuō)明上面的for循環(huán)報(bào)錯(cuò)了,這里將從報(bào)錯(cuò)開(kāi)始的所有值賦值到報(bào)錯(cuò)前調(diào)整過(guò)位置
//的不刪除的值的后面。(這里有點(diǎn)沒(méi)說(shuō)清楚,大家看代碼應(yīng)該能懂 :))
if (r != size) {
System.arraycopy(elementData, r, elementData, w, size - r);
w += size - r;
}
//w不等于size證明removeAll傳入的c不為null。這里從w開(kāi)始,將后面的所有值(上面for循環(huán)將不刪除
//的值全部放到了w前面)賦值為null。
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;
}
removeAll(Collection<?> c)方法的主要解釋都放在了代碼里面,這個(gè)有點(diǎn)繞,大家多看看代碼理解下應(yīng)該沒(méi)什么問(wèn)題。
ArrayList的主要方法這里就分析完成了,我們可以發(fā)現(xiàn)ArrayList在刪除,添加的時(shí)候會(huì)比較麻煩,在查詢的時(shí)候比較簡(jiǎn)單。所以以后再多查詢,少添加,少刪除的時(shí)候可以考慮ArrayList。
上文中如果有錯(cuò)誤的地方,歡迎大家指出,也歡迎大家留言交流。