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