ArrayList,List 接口,是順序容器,即元素存放的數(shù)據(jù)與放進(jìn)去的順序相同,允許放入null元素,底層通過數(shù)組實(shí)現(xiàn)。
transient Object[] elementData; // non-private to simplify nested class access
/**
* The size of the ArrayList (the number of elements it contains).
*
* @serial
*/
private int size;
可以看到成員變量為一個(gè)Object[] (Object數(shù)組),Object[] obj這樣的形式,就是一個(gè)Object數(shù)組構(gòu)成的參數(shù)形式。說明這個(gè)方法的參數(shù)是固定的,是一個(gè)Object數(shù)組,至于這個(gè)數(shù)組中存儲的元素,可以是繼承自O(shè)bject的所有類的對象。
至于 ‘ transien’關(guān)鍵詞,我也不太了解,只知道與反序列化有關(guān),這個(gè)暫時(shí)不做了解;
size代表這個(gè)鏈表所包含元素的個(gè)數(shù);
方法剖析:
1、get()方法
得到下標(biāo)為index的值;
public E get(int index) {
Objects.checkIndex(index, size);
return elementData(index);
}
// cheackIndex 是判斷索引下標(biāo)是否超過了容器;
2、set() 方法
public E set(int index, E element) {
Objects.checkIndex(index, size); //越界檢查
E oldValue = elementData(index);
elementData[index] = element; //可以看到復(fù)制的僅僅是引用;
return oldValue;
}
3、add() 方法
private void add(E e, Object[] elementData, int s) {
//按照順序在尾部加入,故時(shí)間復(fù)雜度為O(1)
if (s == elementData.length)
elementData = grow(); //grow() 函數(shù)即為擴(kuò)容過程;
elementData[s] = e;
size = s + 1;
}
public void add(int index, E element) {
//向指定位置增加元素,故時(shí)間復(fù)雜度為O(n),其它元素向后平移;
rangeCheckForAdd(index);
modCount++;
final int s;
Object[] elementData;
if ((s = size) == (elementData = this.elementData).length)
elementData = grow();
System.arraycopy(elementData, index,
elementData, index + 1,
s - index);
elementData[index] = element;
size = s + 1;
}
-----------
private Object[] grow(int minCapacity) {
return elementData = Arrays.copyOf(elementData,
newCapacity(minCapacity));
}
private Object[] grow() {
return grow(size + 1);
}
//擴(kuò)容后,數(shù)組長度加1;
private int newCapacity(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
//可以看到,新的數(shù)組的長度為舊數(shù)組長度的1.5倍,右移運(yùn)算;
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);
}
---------
// copyof arrays類
public static int[] copyOf(int[] original, int newLength) {
int[] copy = new int[newLength]; //創(chuàng)建了一個(gè)新的數(shù)組
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
-----
//systems類
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
所以ArrayList的擴(kuò)容方法如下:
1、數(shù)組內(nèi)增加一個(gè)元素,若超過其容器長度,則容器長度自動擴(kuò)大1.5倍,插入元素后,其數(shù)組長度加1;
2、過程如下圖:
[注圖片轉(zhuǎn)自 http://www.cnblogs.com/CarpenterLee/p/5419880.html]
4、addAll()方法
addAll()方法能夠一次添加多個(gè)元素,根據(jù)位置不同也有兩個(gè)把本,一個(gè)是在末尾添加的addAll(Collection<? extends E> c)方法,一個(gè)是從指定位置開始插入的addAll(int index, Collection<? extends E> c)方法。跟add()方法類似,在插入之前也需要進(jìn)行空間檢查,如果需要則自動擴(kuò)容(擴(kuò)容為,兩者和的 1.5倍);如果從指定位置插入,也會存在移動元素的情況。
addAll()的時(shí)間復(fù)雜度不僅跟插入元素的多少有關(guān),也跟插入的位置相關(guān)。
public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index);
Object[] a = c.toArray();
modCount++;
int numNew = a.length;
if (numNew == 0)
return false;
Object[] elementData;
final int s;
if (numNew > (elementData = this.elementData).length - (s = size))
elementData = grow(s + numNew);
int numMoved = s - index;
if (numMoved > 0)
System.arraycopy(elementData, index,
elementData, index + numNew,
numMoved);
System.arraycopy(a, 0, elementData, index, numNew);
size = s + numNew;
return true;
}
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
modCount++;
int numNew = a.length;
if (numNew == 0)
return false;
Object[] elementData;
final int s;
if (numNew > (elementData = this.elementData).length - (s = size))
elementData = grow(s + numNew);
//擴(kuò)容為,兩者和的 1.5倍;
System.arraycopy(a, 0, elementData, s, numNew);
size = s + numNew;
return true;
}
5、remove() 方法
public void remove() {
if (lastRet < 0)
throw new IllegalStateException(); //拋出異常
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
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
}
public void clear() { //清空
modCount++;
final Object[] es = elementData;
for (int to = size, i = size = 0; i < to; i++)
es[i] = null; // 每個(gè)都賦值為null
}
關(guān)于Java GC這里需要特別說明一下,有了垃圾收集器并不意味著一定不會有內(nèi)存泄漏。對象能否被GC的依據(jù)是是否還有引用指向它,上面代碼中如果不手動賦null值,除非對應(yīng)的位置被其他元素覆蓋,否則原來的對象就一直不會被回收。
6、contains() 方法
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
/**
* Returns the index of the first occurrence of the specified element
* in this list, or -1 if this list does not contain the element.
* More formally, returns the lowest index {@code i} such that
* {@code Objects.equals(o, get(i))},
* or -1 if there is no such index.
*/
public int indexOf(Object o) { //時(shí)間復(fù)雜度為O(n)
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;
}