ArrayList中elementData爲何被transient修飾?

  Java的ArrayList中,定義了一個數組elementData用來裝載對象的,具體定義以下:java

/**
 * The array buffer into which the elements of the ArrayList are stored.
 * The capacity of the ArrayList is the length of this array buffer. Any
 * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
 * will be expanded to DEFAULT_CAPACITY when the first element is added.
 */
transient Object[] elementData; // non-private to simplify nested class access

transient用來表示一個域不是該對象序行化的一部分,當一個對象被序行化的時候,transient修飾的變量的值是不包括在序行化的表示中的。可是ArrayList又是可序行化的類,elementData是ArrayList具體存放元素的成員,用transient來修飾elementData,豈不是反序列化後的ArrayList丟失了原先的元素?
       其實玄機在於ArrayList中的兩個方法:數組

/**
 * Save the state of the <tt>ArrayList</tt> instance to a stream (that
 * is, serialize it).
 *
 * @serialData The length of the array backing the <tt>ArrayList</tt>
 *             instance is emitted (int), followed by all of its elements
 *             (each an <tt>Object</tt>) in the proper order.
 */
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 size as capacity for behavioural compatibility with clone()
    s.writeInt(size);
 
    // 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();
    }
}

 

/**
 * Reconstitute the <tt>ArrayList</tt> instance from a stream (that is,
 * deserialize it).
 */
private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
    elementData = EMPTY_ELEMENTDATA;
 
    // Read in size, and any hidden stuff
    s.defaultReadObject();
 
    // Read in capacity
    s.readInt(); // ignored
 
    if (size > 0) {
        // be like clone(), allocate array based upon size not capacity
        ensureCapacityInternal(size);
 
        Object[] a = elementData;
        // Read in all elements in the proper order.
        for (int i=0; i<size; i++) {
            a[i] = s.readObject();
        }
    }
}

       ArrayList在序列化的時候會調用writeObject,直接將size和element寫入ObjectOutputStream;反序列化時調用readObject,從ObjectInputStream獲取size和element,再恢復到elementData。
       爲何不直接用elementData來序列化,而採用上訴的方式來實現序列化呢?緣由在於elementData是一個緩存數組,它一般會預留一些容量,等容量不足時再擴充容量,那麼有些空間可能就沒有實際存儲元素,採用上訴的方式來實現序列化時,就能夠保證只序列化實際存儲的那些元素,而不是整個數組,從而節省空間和時間。緩存

 

transient分析
注意到的是ArrayList和LinkedList中的一些變量被transient關鍵字修飾。如ArrayList中的elementData數組,LinkedList中指向頭結點和尾結點的指針等。下面解釋一下transient關鍵字的做用:this

java的serialization提供了一種持久化對象實例的機制。當持久化對象時,可能有一個特殊的對象數據成員,我不想用serialization機制來保存它。爲了在一個特定對象的域上關閉serialization,能夠在這個域前加上關鍵字transient。transient是一個關鍵字,用來表示一個於不是該對象串行化的一部分。當一個對象被串行化的時候,被transient關鍵字修飾的變量的值不包括在串行化的表示中,非transient型的變量是被包括進去的。指針

那麼既然用於保存數據的變量都被transient修飾,ArrayList和LinkedList還能不能被序列化呢?code

答案是能夠的。對於ArrayList來講,若是不把elementData申明爲transient類型,那麼序列化的時候裏面的數據都會被序列化,可是elementData這個數組很大程序是存在空值的狀況(即size《length),這時若是序列化就會致使磁盤空間被浪費。爲了解決這個問題,ArrayList將elementData申明爲transient,本身重寫了writeObject方法,保證只序列化elementData中有數據的那部分,ArrayList中的writeObject方法以下:對象

(見上文)ci

注意的是這裏面也用到了modCount,若是不一致說明這段時間集合發生了改變,拋出異常。element

LinkedList中的序列化和ArrayList中的序列化還不同,LinkedList中不會存在說有多餘的元素這種說法,可是因爲LinkedList中申明爲transient類型的數據均可以不用進行序列化,因此進行申明,好比分別指向頭結點和尾結點的first和last指針。it

相關文章
相關標籤/搜索