單鏈表,通俗講就是隻知道下個節點,不知道上個節點,以下圖:
單鏈表的特色:java
雙鏈表,通俗講就是既知道下個節點,也知道上個節點,以下圖:
特色:node
如下都是基於jdk1.8併發
LinkedList是一個雙向鏈表結構 實現了List和Deque接口。 因此也實現了List的操做和雙端隊列的操做,並容許全部元素(包括null ) 迭代操做iterator和listIterator,面對併發修改,迭代器將快速而乾淨地失敗,而不是在將來未肯定的時間冒着任意的非肯定性行爲
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable {}
說明:性能
//鏈表中元素個數 transient int size = 0; //第一個元素 transient Node<E> first; //最後一個元素 transient Node<E> last;
Node中存放元素,Node的源碼:this
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; } }
public boolean add(E e) { linkLast(e); return true; } //在最後節點添加元素 void linkLast(E e) { final Node<E> l = last; //新建節點,指向last節點,並賦值e,next指向null final Node<E> newNode = new Node<>(l, e, null); last = newNode; if (l == null) first = newNode; else l.next = newNode; size++; modCount++; }
這種方式須要遍歷鏈表來獲取該位置的元素,效率較差code
public void add(int index, E element) { checkPositionIndex(index); if (index == size)//若是是尾部 linkLast(element); else linkBefore(element, node(index));//計算index位置並添加 } /** * 根據索引計算位置,返回非空元素 * 這裏是先計算索引位置在鏈表的一半以前仍是以後,而後選擇從前遍歷仍是從後遍歷。這樣作能提高查找效率 */ Node<E> node(int index) { //若是索引值小於list的size一半,就從頭部開始遍歷 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; } } /** * 某個節點succ以前添加節點 */ void linkBefore(E e, Node<E> succ) { final Node<E> pred = succ.prev; //新建節點,前一節點爲索引所在節點的pred,賦值爲元素e,下一節點爲索引所在節點 final Node<E> newNode = new Node<>(pred, e, succ); succ.prev = newNode; if (pred == null) first = newNode; else pred.next = newNode; size++; modCount++; }
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; }
public E remove() { return removeFirst(); } // 移除first public E removeFirst() { final Node<E> f = first; if (f == null) throw new NoSuchElementException(); return unlinkFirst(f); } private E unlinkFirst(Node<E> f) { 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; }
public E remove(int index) { checkElementIndex(index); return unlink(node(index));//計算索引位置元素,性能差 }
public E peek() { final Node<E> f = first; return (f == null) ? null : f.item; }
public E element() { return getFirst(); }
public E poll() { final Node<E> f = first; return (f == null) ? null : unlinkFirst(f); }
//頭部添加 public boolean offer(E e) { return add(e); } //頭部添加 public boolean offerFirst(E e) { addFirst(e); return true; }
public boolean offerLast(E e) { addLast(e); return true; }
由於LinkedList的元素(size、first、last)都用transient
修飾,因此定義了自定義序列化,爲了防止全部空間都序列化,形成空間浪費,因此自定義實現,只序列化有值的鏈表數據blog
private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { s.defaultWriteObject(); // 序列化大小 s.writeInt(size); // 只序列化有值的鏈表數據 for (Node<E> x = first; x != null; x = x.next) s.writeObject(x.item); }