java.util.ArrayList

java.util.ArrayList.java

 

疑問:

  1. transient

    在這裏類中發現了以前沒有見過的transient關鍵字。這個關鍵字的主要標示這個屬性不需要自動序列化。因爲ArrayList實現了java.io.Serializable接口,所以ArrayList對象可以序列化到持久存儲介質中。在這個類中,serialVersionUIDsize都將自動序列化到介質中,但elementData數組對象卻定義爲transient了。也就是說 ArrayList中的所有這些元素都不會自動系列化到介質中。爲什麼要這樣實現?因爲elementData數組中存儲的“元素”其實僅是對這些元素的 一個引用,並不是真正的對象,序列化一個對象的引用是毫無意義的,因爲序列化是爲了反序列化,當你反序列化時,這些對象的引用已經不可能指向原來的對象 了。所以在這兒需要手工的對ArrayList的元素進行序列化操作。這就是writeObject()的作用。

  2. modCount

    在這個類中,有很多地方出現了modCount這個變量。這個變量是在該類的超類AbstractArrayList中定義的。它是用來記錄了ArrayList結構性變化的次數的。那麼這個結構性變化的次數有什麼用呢?還是在AbstractArrayList中,可以利用iterator()方法生成一個實現了Iterator接口的Itr對象。因爲這個Itr類是AbstractArrayList的一個私有內部類,所以這個類可以自由的訪問AbstractArrayList的屬性和方法。Itr對象在初始化的時候,初始化了其內部的一個屬性:int expectedModCount = modCount;當我們遍歷這個對象內部的屬性的時候,需要比較這 expectedModCountmodCount的值是否相等,以確保我們遍歷的是同一個對象。這個可能在多線程的編程中非常有用。假如我們在一個線程中遍歷這個集合,而在遍歷的過程中另外的一個線程修改了集合的結構,這樣就會拋出ConcurrentModificationException異常。

 

構造函數:

ArrayList一共三個構造函數:

public ArrayList(int initialCapacity)
{
    super();
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
    this.elementData = new Object[initialCapacity];
}

根據給定數值initialCapacity初始化initialCapacity大小的ArrayList

public ArrayList()
{
    this(10);
}

默認初始化長度爲10ArrayList

public ArrayList(Collection<? extends E> c)
{
    elementData = c.toArray();
    size = elementData.length;
    // c.toArray might (incorrectly) not return Object[] (see
    //6260652)
    if (elementData.getClass() != Object[].class)
        elementData = Arrays.copyOf(elementData, size, Object[].class);
}

由已存在的Collection構造並初始化一個新的ArrayList

 

主要方法:

  1. public void trimToSize()

             {
                modCount++;
                int oldCapacity = elementData.length;
                if (size < oldCapacity) {
                    elementData = Arrays.copyOf(elementData, size);
                }
         }

釋放多餘的空間。如果存入的元素大小小於分配的空間大小,則將釋放掉空餘的空間。該方法會引起ArrayList實例結構改變,因此modCount需要加1

 

  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

 

  1.  

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;
}

public int lastIndexOf(Object o)
{
        if (o == null)

        {
            for (int i = size-1; i >= 0; i--)
                if (elementData[i]==null)
                    return i;

        }
        else
        {
            for (int i = size-1; i >= 0; i--)
                if (o.equals(elementData[i]))
                    return i;

        }
        return -1;
}

 

返回元素在結構中的位置。從程序中可以看出來indexOf返回的是該元素第一次出現的位置,而lastIndexOf則返回該元素在實例中最後一次出現的位置。如果給定的求解元素爲null的話,也會得到第一個或者最後一個爲null的位置。

 

  1. 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實例中的元素和原實例中的元素是同一個元素,是共享的,改變一個就會改變另外一個。因此使用該方法的時候要加以注意。]

 

  1. 增加元素

    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接口,因此該類可以被序列化。這兩個方法就是序列化的寫和讀方法。在寫方法中需要注意一點,在真正執行寫之前,方法保存了modCountcopy,在寫完之後會用該copymodCount進行比較。如果不一致則拋出 ConcurrentModificationException異常。這是非常有意思的一點。該過程保證了待寫元素在寫的過程中沒有被更改。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章