单链表,通俗讲就是只知道下个节点,不知道上个节点,以下图:
单链表的特色: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); }