LinkedList是List接口的第二个具体的实现类,第一个是ArrayList,前面一篇文章已经总结过了,下面咱们来结合源码,学习LinkedList。java
基于双向链表实现node
便于插入和删除,不便于遍历数组
非线程安全安全
有序(链表维护顺序)数据结构
...源码分析
上面是LinkedList的一些特性。学习
源码以下所示:this
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable
初步分析:线程
思考:code
咳咳,先谷歌一下,发现Deque的意思是双端队列,这里已经能够看出LinkedList是基于双向链表的一些端倪了,带着这点疑问,咱们继续往下看。
源码以下所示:
transient int size = 0; transient Node<E> first; transient Node<E> last; private static final long serialVersionUID = 876323262645176354L;
比ArrayList的成员变量少了好几个呢。
初步分析:
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; } }
注意:此处只截取了Node的代码,Node是LinkedList的静态内部类,仍是在LinkedList.class文件内部的
分析:
也就是说,LinkedList的每个元素都是一个Node,而每个Node都储存了三部份内容,由此也就证明了LinkedList是基于双向链表的。
源码以下所示:
public LinkedList() { } public LinkedList(Collection<? extends E> c) { this(); addAll(c); }
分析:
能够发现,相对于ArrayList,LinkedList类并无指定容量的构造,这是为何呢?
思考:
1. 这就是ArrayList和LinkedList底层依赖不一样有关系,ArrayList底层是数组,LinkedList底层是双向链表。数组初始化是须要声明长度的,链表则不须要。
2. 传入子类进行构造时,也是调用了无参构造方法,再调用addAll()方法,将全部元素添加进去
源码以下所示:
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++; }
addFirst()方法是在链表头部插入一个元素,分析以下:
源码以下:
public boolean add(E e) { linkLast(e); return true; } 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++; }
add()方法默认是在链表的尾部进行添加元素。
分析:
源码以下所示:
public E get(int index) { checkElementIndex(index); return node(index).item; }
get()方法内隐藏着LinkedList不便于进行遍历的真相!必定要搞明白哦。
分析:
node()方法源码以下:
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; } }
分析以下:
能够看出,当你访问的元素越靠近链表的中间,那么获取该元素所花费的时间就会越长,因此LinkedList在遍历上是比较慢的,链表自己是不支持任意性访问的,虽然LinkedList的get()方法能够读到相应元素,可是效率很低,不建议使用。
源码以下所示:
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方法源码以下所示:
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; }
哇,unlink方法源代码有点长啊,容我慢慢道来:
源码以下所示:
public E removeFirst() { final Node<E> f = first; if (f == null) throw new NoSuchElementException(); return 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; }
分析:
LinkedList能够做为FIFO(First In First Out)的队列,也就是先进先出的队列使用,如下是关于队列的操做。
//获取队列的第一个元素,若是为null会返回null public E peek() { final Node<E> f = first; return (f == null) ? null : f.item; } //获取队列的第一个元素,若是为null会抛出异常 public E element() { return getFirst(); } //获取队列的第一个元素,若是为null会返回null public E poll() { final Node<E> f = first; return (f == null) ? null : unlinkFirst(f); } //获取队列的第一个元素,若是为null会抛出异常. public E remove() { return removeFirst(); } //将元素添加到队列尾部 public boolean offer(E e) { return add(e); }
LinkedList也能够做为栈使用,栈的特性是LIFO(Last In First Out),也就是后进先出。 添加和删除元素都只操做队列的首节点便可。
源码以下:
public boolean offerFirst(E e) { addFirst(e); return true; } public boolean offerLast(E e) { addLast(e); return true; } public E peekFirst() { final Node<E> f = first; return (f == null) ? null : f.item; } public E peekLast() { final Node<E> l = last; return (l == null) ? null : l.item; } public E pollFirst() { final Node<E> f = first; return (f == null) ? null : unlinkFirst(f); } public E pollLast() { final Node<E> l = last; return (l == null) ? null : unlinkLast(l); } public void push(E e) { addFirst(e); } public E pop() { return removeFirst(); } public boolean removeFirstOccurrence(Object o) { return remove(o); } public boolean removeLastOccurrence(Object o) { if (o == null) { for (Node<E> x = last; x != null; x = x.prev) { if (x.item == null) { //调用unlink方法删除指定节点 unlink(x); return true; } } } else { for (Node<E> x = last; x != null; x = x.prev) { if (o.equals(x.item)) { unlink(x); return true; } } } return false; }
LinkedList相对于ArrayList而言,源码并无很复杂,从源码中咱们得知了如下相关信息:
知之为知之,不知为不知,是知也。