为什么ArrayList存放内容的数组声明为transient
/**
* 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
因为elementData数组中的每个元素并不一定都存储了数据。如果采用外部序列化的方式,则会序列化整个数据。ArrayList为了避免这些没有存储内容的数组被序列化,重新实现了writeObject和readObject方法来实现自我序列化和反序列化。
因此使用 transient 修饰数组,是防止对象数组被其他外部方法序列化。
我们在使用 ArrayList 进行新增、删除时,经常被提醒“使用 ArrayList 做新增删除操作会影响效率”。那是不是 ArrayList 在大量新增元素的场景下效率就一定会变慢呢
如果我们在初始化时就比较清楚存储数据的大小,就可以在 ArrayList 初始化时指定数组容量大小,并且在添加元素时,只在数组末尾添加元素,那么 ArrayList 在大量新增元素的场景下,性能并不会变差,反而比其他 List 集合的性能要好。
LinkedList实现原理
LinkedList是基于双向链表来实现的,链表节点的定义如下:
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
另外,也定义了两个Node类型的first和last节点,分别代表链表的头结点跟尾结点。这样做有如下好处:
- first/last 属性能更清晰地表达链表的链头和链尾概念;
- first/last 方式可以在初始化 LinkedList 的时候节省 new 一个 Entry;
- first/last 方式最重要的性能优化是链头和链尾的插入删除操作更加快捷了。
LinkedList属性
LinkedList 的两个重要属性 first/last 属性,其实还有一个 size 属性。我们可以看到这三个属性都被 transient 修饰了,原因很简单,我们在序列化的时候不会只对头尾进行序列化,所以 LinkedList 也是自行实现 readObject 和 writeObject 进行序列化与反序列化。
transient int size = 0;
/**
* Pointer to first node.
* Invariant: (first == null && last == null) ||
* (first.prev == null && first.item != null)
*/
transient Node<E> first;
/**
* Pointer to last node.
* Invariant: (first == null && last == null) ||
* (last.next == null && last.item != null)
*/
transient Node<E> last;
writeObject方法实现
/**
* Saves the state of this {@code LinkedList} instance to a stream
* (that is, serializes it).
*
* @serialData The size of the list (the number of elements it
* contains) is emitted (int), followed by all of its
* elements (each an Object) in the proper order.
*/
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException {
// Write out any hidden serialization magic
s.defaultWriteObject();
// Write out size
s.writeInt(size);
// Write out all elements in the proper order.
for (Node<E> x = first; x != null; x = x.next)
s.writeObject(x.item);
}
readObject方法实现
/**
* Reconstitutes this {@code LinkedList} instance from a stream
* (that is, deserializes it).
*/
@SuppressWarnings("unchecked")
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
// Read in any hidden serialization magic
s.defaultReadObject();
// Read in size
int size = s.readInt();
// Read in all elements in the proper order.
for (int i = 0; i < size; i++)
linkLast((E)s.readObject());
}