LinkedList的底层是一个双向链表结构,所以他具备链表的特性,插入删除快,查找慢。更有意思的是LinkedList能够认为是集链表与队列与一身的一个实现类。下面经过阅读源码来理解一下。java
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内部维护一个Node类,从代码上可知该类表示一个双向链表。node
transient int size = 0;
transient Node<E> first;
transient Node<E> last;
复制代码
size 表示当前链表的大小, first表示头节点 last表示尾节点缓存
public LinkedList() {
}
复制代码
默认无参构造函数,什么都不作。函数
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
复制代码
经过一个集合来构造,首先建立一个空的本身,而后将集合c追加到本身的末尾。优化
public boolean add(E e) {
linkLast(e);
return true;
}
复制代码
追加到链表末尾。this
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
复制代码
逻辑:校验index是否合法,不然抛出越界异常。若是index是最后一个位置,那么调用linkLast方法追加到原链表最后,不然插在指定位置。 这里有两个私有方法须要看一下spa
Node<E> node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
复制代码
根据索引index,获取指定位置的节点。这里作了一个优化,若是index在前半部分,那么就从first开始往下找,若是index在后半部分,那就从last往前开始找。指针
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);
succ.prev = newNode;
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
modCount++;
}
复制代码
在succ节点以前插入一个节点,该节点元素值为e。 逻辑:先找到原succ节点的前一个节点,缓存下来,记为pred。新建一个节点,该 节点的元素值为e,prev指针指向pred节点,next指针指向succ节点。succ节点的prev指针指向新节点. 此时须要判断,若是原来succ节点是头结点,那么新添加的节点为新的first头结点。不然的话, 原来的pred节点的prev指针指向我这个新节点,那么我就插入到了pred节点和succ节点的中间。code
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
复制代码
来看私有方法 unlink索引
E unlink(Node<E> x) {
// assert x != null;
final E element = x.item;
final Node<E> next = x.next;
final Node<E> prev = x.prev;
if (prev == null) {
first = next;
} else {
prev.next = next;
x.prev = null;
}
if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
}
x.item = null;
size--;
modCount++;
return element;
}
复制代码
逻辑:先缓存下我这个节点的prev和next节点. 若是原来我是头节点first,那么只需把first节点指向个人next,我就从原始链表中脱离了。不然的话个人prev节点的next指针指向个人next节点。
若是原来我是尾节点last,那么只需把last节点指向个人prev,我就从原始链表中脱离了。不然的话,个人next节点的prev指针指向个人prev节点.
脱离链表后,须要把个人元素置为null,不然会形成内存泄露。
public boolean remove(Object o) {
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null) {
unlink(x);
return true;
}
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
}
复制代码
逻辑类似,经过从头节点遍历的方式查找到要删除的第一个节点,而后调用unlink方法进行移除.
public int indexOf(Object o) {
int index = 0;
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null)
return index;
index++;
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item))
return index;
index++;
}
}
return -1;
}
复制代码
顺序查找,找到第一个就返回该值索引,若是没找到就返回-1.
public int lastIndexOf(Object o) {
int index = size;
if (o == null) {
for (Node<E> x = last; x != null; x = x.prev) {
index--;
if (x.item == null)
return index;
}
} else {
for (Node<E> x = last; x != null; x = x.prev) {
index--;
if (o.equals(x.item))
return index;
}
}
return -1;
}
复制代码
倒序查找,找到第一个就返回该值索引,若是没找到就返回-1.
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
复制代码
获取指定位置的元素值。
public E set(int index, E element) {
checkElementIndex(index);
Node<E> x = node(index);
E oldVal = x.item;
x.item = element;
return oldVal;
}
复制代码
修改指定位置的元素值,返回旧值。
以上的增长、删除、查找接口都是实现List接口,而LinkedList不只实现了List接口,还实现的Deque接口,双向队列。所以它做为一个双向队列,还提供了相应的增删查改接口。
队列的特色是先入先出。因此不会提供指定位置的增删查改功能,只能读写头尾节点。
public void addFirst(E e) {
linkFirst(e);
}
复制代码
public boolean offer(E e) {
return add(e);
}
复制代码
public boolean offerFirst(E e) {
addFirst(e);
return true;
}
复制代码
public void push(E e) {
addFirst(e);
}
复制代码
私有方法 linkFirst
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++;
}
复制代码
逻辑:新建一个节点,该节点的元素值为e,prev指针为null,next指针为原来头结点。而后再把first头结点指针指向新节点。此处须要注意的是若是原来没有节点,当前新增节点为第一个节点,那么last指针也指向该节点。
public void addLast(E e) {
linkLast(e);
}
复制代码
私有方法 linkLast
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++;
}
复制代码
逻辑:新建一个节点,该节点的元素值为e,prev指针为原来的尾节点,next指针为null,而后再把last指针指向该节点。此处须要注意的是,若是原来没有节点,当前新增节点为第一个节点,那么first指针也指向该节点。
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
复制代码
public E pop() {
return removeFirst();
}
复制代码
public E pollFirst() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
复制代码
public E poll() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
复制代码
来看私有方法unlinkFirst
private E unlinkFirst(Node<E> f) {
// assert f == first && f != null;
final E element = f.item;
final Node<E> next = f.next;
f.item = null;
f.next = null; // help GC
first = next;
if (next == null)
last = null;
else
next.prev = null;
size--;
modCount++;
return element;
}
复制代码
逻辑:要移除一个节点,则将该节点的prev指针、next指针都置为null,那么当前节点就从原链表中脱离了。同时要将这个节点的元素置为null,不然会形成内存泄露. 再将first节点置为原节点的next。此时须要注意若是原来只有一个节点,移除以后last节点变成了null.
public E removeLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return unlinkLast(l);
}
复制代码
public E pollLast() {
final Node<E> l = last;
return (l == null) ? null : unlinkLast(l);
}
复制代码
来看私有方法unlinkLast
private E unlinkLast(Node<E> l) {
// assert l == last && l != null;
final E element = l.item;
final Node<E> prev = l.prev;
l.item = null;
l.prev = null; // help GC
last = prev;
if (prev == null)
first = null;
else
prev.next = null;
size--;
modCount++;
return element;
}
复制代码
相同的逻辑,该节点的prev指针、next指针均置为null,则该节点从原链表中脱离出来,此时须要将该节点的元素置为null,避免内存泄露。 同时须要注意若是原链表只有这一个节点,那么移除该节点后,first节点指向null。
public E peek() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
复制代码
public E element() {
return getFirst();
}
复制代码
public E peekFirst() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
复制代码
public E pollFirst() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
复制代码