java.util.ArrayList.java:
疑問:
-
transient
在這裏類中發現了以前沒有見過的transient關鍵字。這個關鍵字的主要標示這個屬性不需要自動序列化。因爲ArrayList實現了java.io.Serializable接口,所以ArrayList對象可以序列化到持久存儲介質中。在這個類中,serialVersionUID和size都將自動序列化到介質中,但elementData數組對象卻定義爲transient了。也就是說 ArrayList中的所有這些元素都不會自動系列化到介質中。爲什麼要這樣實現?因爲elementData數組中存儲的“元素”其實僅是對這些元素的 一個引用,並不是真正的對象,序列化一個對象的引用是毫無意義的,因爲序列化是爲了反序列化,當你反序列化時,這些對象的引用已經不可能指向原來的對象 了。所以在這兒需要手工的對ArrayList的元素進行序列化操作。這就是writeObject()的作用。
-
modCount
在這個類中,有很多地方出現了modCount這個變量。這個變量是在該類的超類AbstractArrayList中定義的。它是用來記錄了ArrayList結構性變化的次數的。那麼這個結構性變化的次數有什麼用呢?還是在AbstractArrayList中,可以利用iterator()方法生成一個實現了Iterator接口的Itr對象。因爲這個Itr類是AbstractArrayList的一個私有內部類,所以這個類可以自由的訪問AbstractArrayList的屬性和方法。Itr對象在初始化的時候,初始化了其內部的一個屬性:int expectedModCount = modCount;當我們遍歷這個對象內部的屬性的時候,需要比較這 expectedModCount和modCount的值是否相等,以確保我們遍歷的是同一個對象。這個可能在多線程的編程中非常有用。假如我們在一個線程中遍歷這個集合,而在遍歷的過程中另外的一個線程修改了集合的結構,這樣就會拋出ConcurrentModificationException異常。
構造函數:
ArrayList一共三個構造函數:
public ArrayList(int initialCapacity) |
根據給定數值initialCapacity初始化initialCapacity大小的ArrayList |
public ArrayList() |
默認初始化長度爲10的ArrayList |
public ArrayList(Collection<? extends E> c) |
由已存在的Collection構造並初始化一個新的ArrayList |
主要方法:
-
public void trimToSize()
{
modCount++;
int oldCapacity = elementData.length;
if (size < oldCapacity) {
elementData = Arrays.copyOf(elementData, size);
}
}
釋放多餘的空間。如果存入的元素大小小於分配的空間大小,則將釋放掉空餘的空間。該方法會引起ArrayList實例結構改變,因此modCount需要加1。
-
public void ensureCapacity(int minCapacity)
{
modCount++;
int oldCapacity = elementData.length;
if (minCapacity > oldCapacity)
{
Object oldData[] = elementData;
int newCapacity = (oldCapacity * 3)/2 + 1;
if (newCapacity < minCapacity)
newCapacity = minCapacity;
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
}
增加ArrayList實例的容量。參數minCapacity是實例擴容時所必須保證的最小值。從程序上來看,每次的擴容實際上增加的是原先實例容量的1.5倍再加1。該方法會引起ArrayList實例結構的改變,所以modCount自加1。
public int indexOf(Object o) |
public int lastIndexOf(Object o) |
返回元素在結構中的位置。從程序中可以看出來indexOf返回的是該元素第一次出現的位置,而lastIndexOf則返回該元素在實例中最後一次出現的位置。如果給定的求解元素爲null的話,也會得到第一個或者最後一個爲null的位置。
-
public Object clone()
{
try
{
ArrayList<E> v = (ArrayList<E>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e)
{
// this shouldn't happen, since we are Cloneable
throw new InternalError();
}
}
方法名顧名思義,克隆現有的ArrayList實例。但是這裏需要注意的是對實力元素的copy是一種淺copy,也就是說新克隆出的ArrayList實例中的元素和原實例中的元素是同一個元素,是共享的,改變一個就會改變另外一個。因此使用該方法的時候要加以注意。]
-
增加元素
public boolean add(E e)
{
ensureCapacity(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}增加一個元素,在增加前先對容量進行修改,在這裏會修改實例的結構,因此modCount會加1,然後將增加的元素加入到實例的末尾。
public void add(int index, E element)
{
if (index > size || index < 0) throw new IndexOutOfBoundsException( "Index: "+index+", Size: "+size);
ensureCapacity(size+1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1, size - index);
elementData[index] = element;
size++;
}
在指定位置增加元素。和上面的方法一樣,首先對現有容量進行修改,同樣也會修改實例的結構,modCount會加1。然後將指定位置之後的所有元素向後順移一位,在將指定元素插入指定位置。這裏注意,如果指定的位置超過了實例的上界和下界將會拋出IndexOutOfBoundsException異常。
public boolean addAll(Collection<? extends E> c)
{
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacity(size + numNew); // Increments modCount
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
將一個實現了Collection接口的類實例中的元素加入到現有的實例中。該操作會影響實例的結構,所以modCount自加1。調用這個方法需要注意一個問題,該操作執行的過程中不會注意的Collection是否已經被修改了。
public boolean addAll(int index, Collection<? extends E> c)
{
if (index > size || index < 0)
throw new IndexOutOfBoundsException(
"Index: " + index + ", Size: " + size);
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacity(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;
}
這個方法和上一個方法差不多,只不過是在指定的位置插入一個實現了Collection接口的類的實例。插入之前,將該指定位置的元素挨個向後移動一位。同樣,該操作會引起實例結構的改變。在執行操作之前,會判斷指定的位置是否合法。
刪除元素
public E remove(int index)
{
RangeCheck(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; // Let gc do its work
return oldValue;
}
移除指定位置上的元素並且返回移除的元素。這個方法會先調用一個叫做RangeCheck的方法,這個方法用來檢查給定的位置是否合法,如果非法,會拋出IndexOutOfBoundsException。該方法會改變實例的結構。移除之後,該位置之後的元素會向前順移一位。源碼中有一個很有意思的註釋:Let gc do its work !這裏可能會產生內存泄露的問題。
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;
}
這個方法是從實例中第0個元素開始查找指定的元素,如果查找到則移除指定的元素成功,否則失敗。注意指定元素爲null也可以被移除。在移除元素的時候,該方法將找到的指定元素的位置傳遞給fastRemove方法。
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; // Let gc do its work
}
該方法和public E remove(int index)方法的實現方式是一樣的,只不過該方法不會返回被刪除的元素。
protected void removeRange(int fromIndex, int toIndex)
{
modCount++;
int numMoved = size - toIndex;
System.arraycopy(elementData, toIndex, elementData, fromIndex,
numMoved);
// Let gc do its work
int newSize = size - (toIndex-fromIndex);
while (size != newSize)
elementData[--size] = null;
}
移除指定位置之間的所有元素(前閉後開)。該方法會引起結構變動。
public void clear()
{
modCount++;
// Let gc do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
清除實例中的所有元素。該操作會引起實例結構的變化。
private void writeObject(java.io. ObjectOutputStream s) throws java.io.IOException { // Write out element count, and any //hidden stuff int expectedModCount = modCount; s.defaultWriteObject(); // Write out array length s.writeInt(elementData.length); // Write out all elements in the proper //order. for (int i=0; i<size; i++) s.writeObject(elementData[i]); if (modCount != expectedModCount) { throw new ConcurrentModificationException(); } } |
private void readObject(java.io. ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { // Read in size, and any hidden stuff s.defaultReadObject(); // Read in array length and allocate //array int arrayLength = s.readInt(); Object[] a = elementData = new Object[arrayLength]; // Read in all elements in the proper //order. for (int i=0; i<size; i++) a[i] = s.readObject(); }
|
因爲ArrayList實現了java.io.Serializable接口,因此該類可以被序列化。這兩個方法就是序列化的寫和讀方法。在寫方法中需要注意一點,在真正執行寫之前,方法保存了modCount的copy,在寫完之後會用該copy於modCount進行比較。如果不一致則拋出 ConcurrentModificationException異常。這是非常有意思的一點。該過程保證了待寫元素在寫的過程中沒有被更改。