写在前面的话
本文针对的是Java1.6进行的源码分析,与其他版本可能存在差异。
概述
LinkedList是基于双向链表来实现的,与ArrayList一样,它也实现了List接口。与ArrayList相比,它的插入和删除操作更加高效,但是在随机访问数据方面要逊色许多。LinkedList适用的场景:更多的执行插入和删除操作,基本上不会随机访问数据。
源代码解析
1. LinkedList底层数据结构
private static class Entry<E> {
//存储的数据
E element;
//指向双向链表的后结点
Entry<E> next;
//指向双向链表的前结点
Entry<E> previous;
//构造方法
Entry(E element, Entry<E> next, Entry<E> previous) {
this.element = element;
this.next = next;
this.previous = previous;
}
}
Entry定义了三个属性,element用于存放数据,next指向双向链表的后结点,previous指向链表的前结点。LinkedList是基于双向链表实现的,它定义了一个Entry类型的头结点head,head中并不存储任何数据。如下图:
2. LinkedList定义
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
LinkedList继承了AbstractSequentialList,并且实现了List和Deque接口。AbstractSequentialList实现了List接口中的get()、set()、add()和remove()方法,降低了List接口的复杂度。LinkedList实现了Deque接口,意味着我们可以将它当做双向队列来用。
3. 属性
private transient Entry<E> header = new Entry<E>(null, null, null);
private transient int size = 0;
LinkedList定义了两个属性,header为双向链表的头结点,它并不存储任何数据。size是LinkedList的大小,初始化为0.
4. 构造方法
//默认构造方法,head的前后结点都指向它本身
public LinkedList() {
header.next = header.previous = header;
}
//带有collection参数的构造方法
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
LinkedList提供了两个构造方法,第二个需要调用addAll()方法,我们看一下addAll()的实现:
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
}
public boolean addAll(int index, Collection<? extends E> c) {
//校验传入index的值,失败的话,直接抛出异常
if (index < 0 || index > size)
throw new IndexOutOfBoundsException("Index: "+index+
", Size: "+size);
//将传入的collection转化为数据
Object[] a = c.toArray();
int numNew = a.length;
//如果数组内没有数据,直接返回false
if (numNew==0)
return false;
modCount++;
//如果index和size相等的话,说明是在链表末尾添加数据,否则找到当前index处的结点
Entry<E> successor = (index==size ? header : entry(index));
//找到插入位置的前一个结点
Entry<E> predecessor = successor.previous;
//遍历数组,增加结点
for (int i=0; i<numNew; i++) {
//新建结点,并把结点放在successor和predecessor的中间
Entry<E> e = new Entry<E>((E)a[i], successor, predecessor);
predecessor.next = e;
//predecessor向后移动
predecessor = e;
}
successor.previous = predecessor;
//更新size大小
size += numNew;
return true;
}
//查找index处的结点
private Entry<E> entry(int index) {
//校验传入index的值,失败的话,直接抛出异常
if (index < 0 || index >= size)
throw new IndexOutOfBoundsException("Index: "+index+
", Size: "+size);
Entry<E> e = header;
//判断index和size的关系,如果在链表的前半段,则从头开始查找;
//如果在链表的后半段,则从尾部开始查找
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;
}
entry(int index)方法是用来查找index处的结点的,这个方法比普通的遍历做了个小小的优化。先判断index和size的关系,如果要查找的结点在链表的前半段,则从链表头部开始查找;如果要查找的结点在链表的后半段,则从链表的尾部开始查找。
5.添加数据
LinkedList的add(E e)方法是在链表末尾添加结点,会调用addBefore方法在头结点前插入结点。
public boolean add(E e) {
addBefore(e, header);
return true;
}
//在entry结点前添加结点
private Entry<E> addBefore(E e, Entry<E> entry) {
//新建结点,next指向entry,previous指向entry的前一个结点
Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);
//让新结点的前后结点的对应引用都指向自己
newEntry.previous.next = newEntry;
newEntry.next.previous = newEntry;
size++;
modCount++;
return newEntry;
}
6.获取数据
LinkedList的get(int index)方法是获取在index索引处的值,会调用上面说到的entry(int index)方法,获取index处的结点,返回结点的值。
public E get(int index) {
return entry(index).element;
}
7.删除数据
LinkedList的remove(Object o)方法是删除list中第一个值为o的数据,会先遍历链表,找到第一个值为o的结点,然后在链表中把该结点删除。
public boolean remove(Object o) {
//如果o的值为null,则单独处理
if (o==null) {
for (Entry<E> e = header.next; e != header; e = e.next) {
if (e.element==null) {
remove(e);
return true;
}
}
} else {
//从头遍历链表,找到值为o的结点
for (Entry<E> e = header.next; e != header; e = e.next) {
if (o.equals(e.element)) {
remove(e);
return true;
}
}
}
return false;
}
//链表中删除给定结点方法
private E remove(Entry<E> e) {
//header结点不保存数据,如果是header结点,直接抛出异常
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;
}
8.判断值是否存在
LinkedList的contains(Object o)方法是判断值o在list中是否存在,直接调用indexOf(Object o)方法来判断。
public boolean contains(Object o) {
return indexOf(o) != -1;
}
//获取o在链表中的索引,不存在就返回-1
public int indexOf(Object o) {
int index = 0;
//如果o为null的话,单独处理
if (o==null) {
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;
}