LinkedList源码解析
1.LinkedList概述
要了解LinkedList,我们首先要看一下API(1.6)里面的解释:
List 接口的链接列表实现。实现所有可选的列表操作,并且允许所有元素(包括 null)。
insert 元素提供了统一的命名方法。这些操作允许将链接列表用作堆栈、队列或双端队列。
此类实现 Deque 接口,为 add、poll 提供先进先出队列操作,以及其他堆栈和双端队列操作。
所有操作都是按照双重链接列表的需要执行的。在列表中编索引的操作将从开头或结尾遍历列表(从靠近指定索引的一端)。
在这里我们简单介绍一下链表的概念。
- 单向链表
单向链表的构成有两部分组成,一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针。第一个节点的next指向第二个节点,依次类推,这样子就把数据链在一起了。
如果要访问链表中一个元素,需要从第一个元素始,一直找到需要的元素位置。但是增加和删除一个元素对于链表数据结构就非常简单了,只要修改元素中的指针就可以了。如果应用需要经常插入和删除元素你就需要用链表。
- 单向循环链表
单向循环链表其实比较好理解,就是链表的头和尾链接到了一起,看图片很直观。
- 双向链表
双向链表其实也不难理解,就是在单向链表的基础上,新增加了一个pre指针,用来指向上一个节点。双向链表是包含两个指针的,pre指向前一个节点,next指向后一个节点,但是第一个节点head的pre指向null,最后一个节点的tail指向null。双向链表的优势就是可以访问上一个节点。
- 双向循环链表
第一个节点的pre指向最后一个节点,最后一个节点的next指向第一个节点,也形成一个“环”。
看完了上面对链表的了解,接下来看一下具体LinkedList的继承关系
public class LinkedList<E>extends
AbstractSequentialList<E>implements List<E>, Deque<E>,
Cloneable, Serializable
可以看一下继承图
从结构上,我们还看到了LinkedList实现了Deque接口,因此,我们可以操作LinkedList像操作队列和栈一样~
2.LinkedList属性
上面的图片中黄色的就是属性。
其中first,和last就是头结点和尾节点。
size就是链表的长度,可以看出初始值尾0。
其实我们可以来看一下Node的定义
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;
}
}
从这里我们可以看出LinkedList至少是一个双向链表。
3.LinkedList构造方法
1.LinkedList()
构造一个空列表。具体代码如下:
/**
* Constructs an empty list.感觉这个没什么用☺
*/
public LinkedList() {
}
2.LinkedList(Collection< extends E> c)
接着我们看一下里面传一个集合如何处理?
/**
* Constructs a list containing the elements of the specified
* collection, in the order they are returned by the collection's
* iterator.
*
* @param c the collection whose elements are to be placed into this list
* @throws NullPointerException if the specified collection is null
*/
public LinkedList(Collection<? extends E> c) {
this();//这里的this()其实指的就是无参构造LinkedList() 。
addAll(c);//具体方法在add()方法区演示。
}
4.LinkedList常用方法
1.add()方法
1.1 add(E e)
将指定元素添加到此列表的结尾。
此方法等效于 addLast(E)。
public boolean add(E e) {
linkLast(e);
return true;
}
//我们在看看这个方法linkLast(e)
/**
* Links e as last element.
*/
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
linkLast(e)这个方法的作用就是把新添加的元素存放在元素的最后。先对first节点进行判断,如果为空,就存在第一个。不为空,就存放在last,并且赋值给上一个的next。
1.2 add(int index, E element)
public void add(int index, E element) {
checkPositionIndex(index);//有效位检验
if (index == size)
linkLast(element);//插入的位置和长度一样,就插入到最后一个。
else
linkBefore(element, node(index));
}
这段代码的意思就是将指定的元素插入此列表中的指定位置。 将当前在该位置的元素(如果有的话)和任何后续的元素移到右侧(在其索引中增加一个)。
主要来看一下:linkBefore(E e, Node<E> succ)
方法:
void linkBefore(E e, Node<E> succ) {
// assert succ != null;
final Node<E> pred = succ.prev;
final Node<E> newNode = new Node<>(pred, e, succ);
//就是把插入位置元素的前一个获取。在新建一个节点,pred节点就是
//原来的节点,next节点存储的就是当前位置节点。这个就相当于把新元素插入到这个位置的前面。
succ.prev = newNode;
if (pred == null)
first = newNode;
//查看当前位置是否为第一个元素,如果是,直接复制给first。
else
pred.next = newNode;
//把新建节点赋给前一个元素的next
size++;
modCount++;
}
1.3 addAll(Collection< extends E> c)
添加指定 collection 中的所有元素到此列表的结尾,顺序是指定 collection 的迭代器返回这些元素的顺序。
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
}
这个方法调用了addAll(int index, Collection< extends E> c) ,所以我们直接来看这个方法。
1.4 addAll(int index, Collection< extends E> c)
将指定 collection 中的所有元素从指定位置开始插入此列表。
public boolean addAll(int index, Collection<? extends E> c) {
checkPositionIndex(index);//进行有效位校验
Object[] a = c.toArray();
int numNew = a.length;//获取数组长度
if (numNew == 0)
return false;//检测数组是否为空
Node<E> pred, succ;
if (index == size) {
succ = null;
pred = last;//如果插入位置与长度一致,则添加在最后。
} else {
succ = node(index);
pred = succ.prev;//否则添加到指定位置
}
for (Object o : a) {
@SuppressWarnings("unchecked") E e = (E) o;
Node<E> newNode = new Node<>(pred, e, null);
if (pred == null)
first = newNode;
else
pred.next = newNode;
pred = newNode;
}
//通过foreach()遍历所有的元素,如果pred 不为空,则把新节点添加到pred 的next中
//下面的作用就是判断添加完数组之后后面后还有没有元素,如果有就把succ和之前的pred连接起来。
if (succ == null) {
last = pred;
} else {
pred.next = succ;
succ.prev = pred;
}
size += numNew;
modCount++;
return true;
}
1.5 addFirst(E e)
public void addFirst(E e) {
linkFirst(e);
}
private void linkFirst(E e) {
final Node<E> f = first;
final Node<E> newNode = new Node<>(null, e, f);
first = newNode;
if (f == null)
last = newNode;
else
f.prev = newNode;
size++;
modCount++;
}
这个很简单,就是把新节点给first,如果f不为空,则把新节点赋给f的prev,就是前一个节点。
1.5 addLast(E e)
将指定元素添加到此列表的结尾。等效于 add(E)。
public void addLast(E e) {
linkLast(e);
}
2.remove()方法
2.1 addLast(E e)
public E remove() {
return removeFirst();
}
public E pop() {
return removeFirst();
}
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}