LinkedList源码简单分析
LinkedList的声明
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>/*这是双端队列接口,这个接口扩展了Queue接口,提供了更多的方法,比如push,pop等*/, Cloneable, java.io.Serializable
所以LinkedList可以被用作Stack,Queue和Deque
来看一下链表结点的 定义
private static class Entry<E> {
E element;
Entry<E> next;
Entry<E> previous; //由此可以看出LinkedList是一个双向链表
Entry(E element, Entry<E> next, Entry<E> previous) {
this.element = element;
this.next = next;
this.previous = previous;
}
LinkedList中声明了下面两个实例变量:
//头结点,起标记作用,并不记录元素
private transient Entry<E> header = new Entry<E>(null, null, null);
//链表的大小 ,即链表中元素的个数
private transient int size = 0;
我们可以看到这两个 就是都被声明成了transient,所以在序列化的过程中会忽略它们,但是LinkedList提供的序列化方法writeObject(java.io.ObjectOutputStream s)中却序列化了size,并且将除header之外的所有结点都 写到序列化文件中了,那为什么要把size声明成transient呢,不解。。求解释。。
几个重要的方法:
/**
* Returns the indexed entry.
根据给定的索引值离表头近还是离表尾近决定从头还是从尾开始遍历
*/
private Entry<E> entry(int index) {
if (index < 0 || index >= size)
throw new IndexOutOfBoundsException("Index: "+index+
", Size: "+size);
Entry<E> e = header;
if (index < (size >> 1)) { //如果较靠近有表头
for (int i = 0; i <= index; i++)
e = e.next;
} else { //较靠近表尾
for (int i = size; i > index; i--)
e = e.previous;
}
return e;
}
/**
*将元素e添加到entry结点之前
*/
private Entry<E> addBefore(E e, Entry<E> entry) {
Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);
newEntry.previous.next = newEntry; //将新结点与前后结点相连接
newEntry.next.previous = newEntry;
size++;
modCount++;
return newEntry;
}
/**
*删除给定的结点e
*/
private E remove(Entry<E> e) {
if (e == header)
throw new NoSuchElementException();
E result = e.element;
e.previous.next = e.next;
e.next.previous = e.previous;
e.next = e.previous = null;
e.element = null;
size--;
modCount++;
return result;
}
/**
*从表头开始遍历,返回此元素在表中的第一个位置
*/
public int indexOf(Object o) {
int index = 0;
if (o==null) { //如果传入的元素是null,则不能调用 eqauls方法进行比较
for (Entry e = header.next; e != header; e = e.next) {
if (e.element==null)
return index;
index++;
}
} else {
for (Entry e = header.next; e != header; e = e.next) {
if (o.equals(e.element))
return index;
index++;
}
}
return -1;
}
/**
*默认的添加动作,可以看到这个方法是把新元素添加 到表尾
*/
public boolean add(E e) {
addBefore(e, header); //加到头结点之前 ,即表尾
return true;
}
/**
*默认的删除动作是删除链表的第一个元素,所以说在默认情况下,LinkedList其实扮*演的是一个队列的角色
*/
public E remove() {
return removeFirst();
}
/**
*返回第一个元素
*/
public E peek() {
if (size==0)
return null;
return getFirst();
}
可以看出,如果表为空的时候 ,这个方法并不会抛出异常,而是返回null,而传统的(在Collections中声明的)方法则会抛出异常。相似的方法还有:poll,但请注意pop方法在表空的时候 会抛出异常。
还有一点请注意,如果先返回list的Iterator,之后 又对链表进行了添加删除修改操作,那么如果再使用返回的那个Iterator就会抛出ConcurrentModificationException。
最后看一下LinkedList是如何序列化和反序列化的,如果对这两个反序列化中用到的这两个回调方法有疑问的,可以看我的这篇博客 http://blog.csdn.net/moreevan/article/details/6697777
/**
* Save the state of this <tt>LinkedList</tt> instance to a stream (that
* is, serialize 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 (Entry e = header.next; e != header; e = e.next)
s.writeObject(e.element);
}
/**
* Reconstitute this <tt>LinkedList</tt> instance from a stream (that is
* deserialize it).
*/
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();
// Initialize header
header = new Entry<E>(null, null, null);
header.next = header.previous = header;
// Read in all elements in the proper order.
for (int i=0; i<size; i++)
addBefore((E)s.readObject(), header);
}
综上 ,我们可以看出。LinkedList是一个双向链表,它可以被当成栈,队列或双端队列来使用。它也提供 了随机访问元素的方法,不过这个访问的时间复杂度并不像ArrayList是O(1),而是O(n)。它删除给定位置的元素
的效率其实并不比ArrayList高。但它删除特定元素的效率 要比ArrayList高。
9/17 By KevinJom