Java集合是如何序列化的?
前不久,我在读到TreeSet的源码时看到了如下方所示的一行代码,从这行代码中可以了解到两个很重要的信息:第一,TreeSet是通过NavigableMap
来实现的(默认情况下m = new TreeMap<E,Object>()
,因此默认情况下TreeSet就是通过TreeMap来实现的);第二,m
是transient
。
private transient NavigableMap<E,Object> m;
m
是transient
???什么情况???那序列化之后TreeSet中不是啥都没有了吗???
紧接着还有更大的发现:
ArrayList
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
...
transient Object[] elementData;
...
}
HashMap
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable {
...
transient Node<K,V>[] table;
transient Set<Map.Entry<K,V>> entrySet;
...
}
其它集合类也都出现了这样的情况,不一一赘述。
集合中用来存储元素的数组无一例外都加上了Java关键字transient
,这意味着这些量都将不做序列化,但通过常识我们也能知道,集合是可以序列化的,而且集合都实现了Serializable
接口,那么集合又将是如何序列化的?那Java设计者又为什么要使用transient
关键字呢?
首先解释为什么要使用transient关键字的问题,以ArrayList为例,elementData是一个对象数组,一般情况下该数组不会存满数据,也就是List的size和capacity一般情况下并不相等,因此全序列化的会浪费空间以及增加不必要的开销。
那么集合如何序列化?
集合中有两个私有的方法readObject和writeObject,这两个方法就是完成集合序列化的秘密武器,下方附上ArrayList序列化部分的源码。
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();
}
}
}