1.概览
LinkedList是java的动态数组另一种实现方式,底层是基于双向链表,而不是数组。
public class LinkedList<E>extends AbstractSequentialList<E>implements List<E>, Deque<E>, Cloneable, java.io.Serializable
LinkedList实现了动态数组与双向队列两个接口,提供了两种方法集合,可以用来实现队列、栈之类的功能。
2. 成员变量
先来看成员变量
transient int size = 0;transient Node<E> first;transient Node<E> last;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;}}
链表一般就是有个head的节点就能完成对应的工作。LinkedList实现了双向链表,除了head,还有一个last节点和一个size参数,这主要是为了效率考虑,不然查询一次长度或者尾都得来一次全链路迭代,太慢了。Node内部类就不说了,非常简单的一个节点类。
3. 方法
3.1 构造方法
public LinkedList() {// 此时first=last=null,size=0}public LinkedList(Collection<? extends E> c) {this();addAll(c);}
3.2 添加元素
添加一个元素
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;elsel.next = newNode;size++;modCount++;}
很简单的添加逻辑,再来看一下addAll的实现
public boolean addAll(Collection<? extends E> c) {return addAll(size, c);}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;elsepred.next = newNode;pred = newNode;}if (succ == null) {last = pred;} else {pred.next = succ;succ.prev = pred;}size += numNew;modCount++;return true;}
3.3 删除元素
public E remove(int index) {checkElementIndex(index);return unlink(node(index));}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;}
3.4 修改元素
public E set(int index, E element) {checkElementIndex(index);Node<E> x = node(index);E oldVal = x.item;x.item = element;return oldVal;}
3.5 检索元素
public E get(int index) {checkElementIndex(index);return node(index).item;}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比较值得看的一个方法,java的实现很简单,先判断index是大于当前size的一半还是小于,如果是大于则从尾节点往前否则从首结点往后检索。从代码上看,虽然双向链表的实现让性能快了一点,但还是O(n)的耗时,我觉得后续版本的优化可以向HashMap那样,当判断LinkedList的size大于一个阈值时可以将双向链接改造为红黑树或者跳表,从而实现O(lgn)的性能,当然这样也对空间消耗更多一点。
3.6 清空元素
public void clear() {// Clearing all of the links between nodes is "unnecessary", but:// - helps a generational GC if the discarded nodes inhabit// more than one generation// - is sure to free memory even if there is a reachable Iteratorfor (Node<E> x = first; x != null; ) {Node<E> next = x.next;x.item = null;x.next = null;x.prev = null;x = next;}first = last = null;size = 0;modCount++;}
从代码上看,LinkedList的clear方法是没有内存泄漏问题的,注意有个for循环,这里是为了gc优化。