LinkedList特性 LinkedList内部使用双向链表作为存储结构,LinkedLIst可以理解为链表的扩展对象,简单封装了常用和非常用的操作链表的方法,以及在通过索引获取元素时的简单优化.LinkedList的特点如下:
根据索引在链表中检索数据的时间复杂度时O(n).
在链表的头部和尾部写入或删除元素效率高
实现了List和Duque的全部功能,可以把它当作一个集合或队列使用
链表中允许保存NULL
链表是非线程安全
源码 重要成员变量 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 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; } }
Node的数据结构是一个双向链表
构造函数 1 2 3 4 5 6 7 8 9 public LinkedList () {} public LinkedList (Collection<? entends E> c) { this (); addAll(c); }
常用方法 add、addFirst与addLast 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 public void addFirst (E e) { linkFirst(e); } public void addLast (E e) { linkLast(e); } public boolean add (E e) { linkLast(e); return true ; } 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++; } 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(int index, E element) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 public void add (int index, E element) { checkPositionIndex(index); if (index == size) linkLast(element); else linkBefore(element, node(index)); } private void checkPositionIndex (int index) { if (!isPositionIndex(index)) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); } private boolean isPositionIndex (int index) { return index >= 0 && index <= size; } Node<E> node (int 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; } } void linkBefore (E e, Node<E> succ) { 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++; }
在指定位置add一个元素,其性能要比在头和尾add一个元素满.
原因在于需要遍历链表,找到index位置的元素,然后将要添加的元素、index位置的元素、以及index-1位置的元素重新建立关系.
源码中的优化:
如果index==size,直接添加到尾部,不进行遍历链表.
遍历时,先判断index在链表的前部还是后部,然后采取从前向后遍历还是从后向前遍历.
addAll 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 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; else pred.next = newNode; pred = newNode; } if (succ == null ) { last = pred; } else { pred.next = succ; succ.prev = pred; } size += numNew; modCount++; return true ; }
set(int index, E element) 1 2 3 4 5 6 7 8 9 10 public E set (int index, E element) { checkElementIndex(index); Node<E> x = node(index); E oldVal = x.item; x.item = element; return oldVal; }
removeFirst,removeLast 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 public E removeFirst () { final Node<E> f = first; if (f == null ) throw new NoSuchElementException(); return unlinkFirst(f); } public E removeLast () { final Node<E> l = last; if (l == null ) throw new NoSuchElementException(); return unlinkLast(l); } private E unlinkFirst (Node<E> f) { final E element = f.item; final Node<E> next = f.next; f.item = null ; f.next = null ; first = next; if (next == null ) last = null ; else next.prev = null ; size--; modCount++; return element; } private E unlinkLast (Node<E> l) { final E element = l.item; final Node<E> prev = l.prev; l.item = null ; l.prev = null ; last = prev; if (prev == null ) first = null ; else prev.next = null ; size--; modCount++; return element; }
remove(int index) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public E remove (int index) { checkElementIndex(index); return unlink(node(index)); } E unlink (Node<E> x) { 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; }
在指定位置删除元素,依旧会遍历找到index对应的元素,然后断开这个元素的前后node.对这个元素是否时头节点和尾节点进行了优化.
remove(Object o) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 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 ; }
根据对象删除元素比根据index删除元素的效率可能还要低,因为没办法判断index < (size>>1),就只能从前向后一直遍历.
removeFirstOccurrence和removeLastOccurrence 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 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(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 ; }
这两个方法是JDK1.6提供的,弥补了remove(o)方法无法从链表尾部开始循环带来的极端情况下,时间复杂度为O(n)的问题
clear 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public void clear () { for (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++; }
getFirst,getLast,get 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public E getFirst () { final Node<E> f = first; if (f == null ) throw new NoSuchElementException(); return f.item; } public E getLast () { final Node<E> l = last; if (l == null ) throw new NoSuchElementException(); return l.item; } public E get (int index) { checkElementIndex(index); return node(index).item; }
如果这是一个空链表,这两个方法获取元素会抛出异常
注意,代码中判断的是fitst和last,并不是元素的item,所以链表中是可以保存null的.
其他函数 contains 1 2 3 4 public boolean contains (Object o) { return indexOf(o) != -1 ; }
size 1 2 3 public int size () { return size; }
indexOf 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 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 ; }
从前往后循环,查找匀速出现的第一次索引.
lastIndexOf 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 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 ; }
从后往前循环,查找元素出现的最后一次索引.
Deque方法 Linkedlist不仅实现了链表,还实现了Deque(双端队列)接口.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 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 E remove () { return removeFirst(); } 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 ; } 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(); }
总结
LinkedList内部使用双向链表作为数据结构存储数据,一切符合链表的特性都对它生效;
LinkedList从头部,尾部添加删除元素的时间复杂度是O(1),在中间位置插入或删除元素时不会产生ArrayList的扩容问题,但需要遍历到指定Node;
LinkedList通过index检索元素进行了index < (size >> 1)的优化,但通过object检索元素并没有优化;
LinkedList基于双向链表这种数据结构,对双端队列操作进行了实现;
所以,LinkedList适合在频繁的写入和删除,但检索相对较少的场景。因为写入和删除不会进行扩容,若在头部和尾部写入或删除元素,不会进行检索,时间复杂度是O(1),就算进行检索,经过了index < (size >> 1)的优化,时间复杂度最多会是O(n/2)。