Java集合之LinkedList源码篇 概述 LinkedList使用链表结构存储元素,链表是一种线性的存储结构,将要存储的数据存在一个存储单元中,这个存储单元除了存储的数据意外,还存储下一个存储单元的地址。
LinkedList是双向链表结构,除了存储自身的值之外,还额外存储了前一个和后一个元素的地址。
不过,我们在项目中一般是不会使用到 LinkedList
的,需要用到 LinkedList
的场景几乎都可以使用 ArrayList
来代替,并且,性能通常会更好!就连 LinkedList
的作者约书亚 · 布洛克(Josh Bloch)自己都说从来不会使用 LinkedList
。
底层数据结构 public class LinkedList <E> extends AbstractSequentialList <E> implements List <E>, Deque<E>, Cloneable, java.io.Serializable {...}
继承AbstractSequentialList
,AbstractSequentialList
又继承自AbstractList
。
实现List接口,表明它是一个列表,支持添加、删除、查找等操作,并且可以通过下标进行访问。
实现Deque接口,继承自 Queue
接口,具有双端队列的特性,支持从两端插入和删除元素,方便实现栈和队列等数据结构。
实现Cloneable接口,表明它具有拷贝能力,可以进行深拷贝或浅拷贝操作。
实现Serializable接口,表明它可以进行序列化操作,也就是可以将对象转换为字节流进行持久化存储或网络传输,非常方便。
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; } }
成员变量 transient int size = 0 ; transient Node<E> first; transient Node<E> last;
构造函数 public LinkedList () {} public LinkedList (Collection<? extends E> c) { this (); addAll(c); }
插入元素 add()
方法有两个版本:
add(E e)
:用于在 LinkedList
的尾部插入元素,即将新元素作为链表的最后一个元素,时间复杂度为 O(1)。
add(int index, E element)
:用于在指定位置插入元素。这种插入方式需要先移动到指定位置,再修改指定节点的指针完成插入/删除,因此需要移动平均 n/2 个元素,时间复杂度为 O(n)。
public boolean add (E e) { linkLast(e); return true ; } public void add (int index, E element) { checkPositionIndex(index); if (index == size) linkLast(element); else linkBefore(element, node(index)); }
linkLast()、linkBefore()
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++; } 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++; }
获取元素 LinkedList
获取元素相关的方法一共有 3 个:
getFirst()
:获取链表的第一个元素。
getLast()
:获取链表的最后一个元素。
get(int index)
:获取链表指定位置的元素。
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; }
从get()
方法中可以看到,核心逻辑在于node(int index)
这个方法。
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; } }
从这个方法的实现逻辑可以看出,该方法通过比较索引值与链表size/2
来确定从链表头还是链表尾开始遍历。
如果索引值小于size/2
,则从链表头开始遍历
如果索引值大于等于size/2
,则从链表尾部开始遍历
这样,即使最坏的情况,也只需要O(n/2)的时间内找到目标节点,充分利用了双向链表的特性来提高效率。
删除元素 LinkedList
删除元素相关的方法一共有 5 个:
removeFirst()
:删除并返回链表的第一个元素。
removeLast()
:删除并返回链表的最后一个元素。
remove(Object o)
:删除链表中首次出现的指定元素,如果不存在该元素则返回 false。
remove(int index)
:删除指定索引处的元素,并返回该元素的值。
void clear()
:移除此链表中的所有元素。
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); } 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 ; } public E remove (int index) { checkElementIndex(index); return unlink(node(index)); } 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++; }
从上面几个方法中可以看到,具体的核心逻辑在unlink()
这个方法中。
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; }
unlink()
方法的逻辑如下:
首先获取待删除节点x的前驱 和后继 节点
判断待删除节点是否为头节点或者尾节点 :
如果x是头节点,则将first指向x的后继节点next
如果x是尾节点,则将last指向x的前驱节点prev
如果x不是头节点也不是尾节点,执行下一步操作
将待删除节点x的前驱的后继 指向待删除节点的后继next ,断开x和x.prev之间的链接
将待删除节点 x 的后继的前驱 指向待删除节点的前驱 prev ,断开 x 和 x.next 之间的链接
将待删除节点 x 的元素置空,修改链表长度
遍历链表 LinkedList
的遍历的核心就是它的迭代器的实现。我们一般推荐使用foreach
来进行遍历,因为底层其实就是使用迭代器来进行遍历的。
我们来看一下双向迭代器的实现:
private class ListItr implements ListIterator <E> { private Node<E> lastReturned; private Node<E> next; private int nextIndex; private int expectedModCount = modCount; ListItr(int index) { next = (index == size) ? null : node(index); nextIndex = index; } public boolean hasNext () { return nextIndex < size; } public E next () { checkForComodification(); if (!hasNext()) throw new NoSuchElementException (); lastReturned = next; next = next.next; nextIndex++; return lastReturned.item; } public boolean hasPrevious () { return nextIndex > 0 ; } public E previous () { checkForComodification(); if (!hasPrevious()) throw new NoSuchElementException (); lastReturned = next = (next == null ) ? last : next.prev; nextIndex--; return lastReturned.item; } public int nextIndex () { return nextIndex; } public int previousIndex () { return nextIndex - 1 ; } public void remove () { checkForComodification(); if (lastReturned == null ) throw new IllegalStateException (); Node<E> lastNext = lastReturned.next; unlink(lastReturned); if (next == lastReturned) next = lastNext; else nextIndex--; lastReturned = null ; expectedModCount++; } public void set (E e) { if (lastReturned == null ) throw new IllegalStateException (); checkForComodification(); lastReturned.item = e; } public void add (E e) { checkForComodification(); lastReturned = null ; if (next == null ) linkLast(e); else linkBefore(e, next); nextIndex++; expectedModCount++; } ... }
其中主要的几个个核心方法如下:
从头到尾遍历,包括方法hasNext()
和next()
从尾到头遍历,包括方法hasPrevious()
和previous()
移除遍历过的节点,包括方法remove()
添加或更新节点,包括方法add()
和set()
Queue方法 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); }
Deque方法 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)。
尾部插入/删除 :只需要修改尾结点的指针即可完成插入/删除操作,因此时间复杂度为 O(1)。
指定位置插入/删除 :需要先移动到指定位置,再修改指定节点的指针完成插入/删除,因此需要移动平均 n/2 个元素,时间复杂度为 O(n)。
LinkedList 为什么不能实现 RandomAccess 接口? RandomAccess
是一个标记接口,用来表明实现该接口的类支持随机访问(即可以通过索引快速访问元素)。由于 LinkedList
底层数据结构是链表,内存地址不连续,只能通过指针来定位,不支持随机快速访问,所以不能实现 RandomAccess
接口。
ArrayList和LinkedList的区别 (1)是否保证线程安全: ArrayList
和 LinkedList
都是不同步的,也就是不保证线程安全;
(2)底层数据结构: Arraylist
底层使用的是 Object
数组 ;LinkedList
底层使用的是 双向链表 数据结构(JDK1.6 之前为循环链表,JDK1.7 取消了循环)
(3)存取效率 :ArrayList底层使用数组实现,所以它的查找时间复杂度是O(1),插入和删除时间复杂度是O(n);而LinkedList底层使用链表实现的,所以它的查找时间复杂度是O(n),插入和删除只需要改变元素指针的指向即可,所以是O(1)。
(4)内存空间占用: ArrayList 的空间浪费主要体现在在 list 列表的结尾会预留一定的容量空间,而 LinkedList 的空间花费则体现在它的每一个元素都需要消耗比 ArrayList 更多的空间(因为要存放直接后继和直接前驱以及数据)。
源码 package java.util;import java.util.function.Consumer;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; public LinkedList () { } public LinkedList (Collection<? extends E> c) { this (); addAll(c); } 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++; } 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++; } 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; } 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; } 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 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); } public void addFirst (E e) { linkFirst(e); } public void addLast (E e) { linkLast(e); } public boolean contains (Object o) { return indexOf(o) != -1 ; } public int size () { return size; } public boolean add (E e) { linkLast(e); return true ; } 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 ; } 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 ; } 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++; } 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 void add (int index, E element) { checkPositionIndex(index); if (index == size) linkLast(element); else linkBefore(element, node(index)); } public E remove (int index) { checkElementIndex(index); return unlink(node(index)); } private boolean isElementIndex (int index) { return index >= 0 && index < size; } private boolean isPositionIndex (int index) { return index >= 0 && index <= size; } private String outOfBoundsMsg (int index) { return "Index: " +index+", Size: " +size; } private void checkElementIndex (int index) { if (!isElementIndex(index)) throw new IndexOutOfBoundsException (outOfBoundsMsg(index)); } private void checkPositionIndex (int index) { if (!isPositionIndex(index)) throw new IndexOutOfBoundsException (outOfBoundsMsg(index)); } 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; } } 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 ; } 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 ; } 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(); } 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 ; } public ListIterator<E> listIterator (int index) { checkPositionIndex(index); return new ListItr (index); } private class ListItr implements ListIterator <E> { private Node<E> lastReturned; private Node<E> next; private int nextIndex; private int expectedModCount = modCount; ListItr(int index) { next = (index == size) ? null : node(index); nextIndex = index; } public boolean hasNext () { return nextIndex < size; } public E next () { checkForComodification(); if (!hasNext()) throw new NoSuchElementException (); lastReturned = next; next = next.next; nextIndex++; return lastReturned.item; } public boolean hasPrevious () { return nextIndex > 0 ; } public E previous () { checkForComodification(); if (!hasPrevious()) throw new NoSuchElementException (); lastReturned = next = (next == null ) ? last : next.prev; nextIndex--; return lastReturned.item; } public int nextIndex () { return nextIndex; } public int previousIndex () { return nextIndex - 1 ; } public void remove () { checkForComodification(); if (lastReturned == null ) throw new IllegalStateException (); Node<E> lastNext = lastReturned.next; unlink(lastReturned); if (next == lastReturned) next = lastNext; else nextIndex--; lastReturned = null ; expectedModCount++; } public void set (E e) { if (lastReturned == null ) throw new IllegalStateException (); checkForComodification(); lastReturned.item = e; } public void add (E e) { checkForComodification(); lastReturned = null ; if (next == null ) linkLast(e); else linkBefore(e, next); nextIndex++; expectedModCount++; } public void forEachRemaining (Consumer<? super E> action) { Objects.requireNonNull(action); while (modCount == expectedModCount && nextIndex < size) { action.accept(next.item); lastReturned = next; next = next.next; nextIndex++; } checkForComodification(); } final void checkForComodification () { if (modCount != expectedModCount) throw new ConcurrentModificationException (); } } 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 Iterator<E> descendingIterator () { return new DescendingIterator (); } private class DescendingIterator implements Iterator <E> { private final ListItr itr = new ListItr (size()); public boolean hasNext () { return itr.hasPrevious(); } public E next () { return itr.previous(); } public void remove () { itr.remove(); } } @SuppressWarnings("unchecked") private LinkedList<E> superClone () { try { return (LinkedList<E>) super .clone(); } catch (CloneNotSupportedException e) { throw new InternalError (e); } } public Object clone () { LinkedList<E> clone = superClone(); clone.first = clone.last = null ; clone.size = 0 ; clone.modCount = 0 ; for (Node<E> x = first; x != null ; x = x.next) clone.add(x.item); return clone; } public Object[] toArray() { Object[] result = new Object [size]; int i = 0 ; for (Node<E> x = first; x != null ; x = x.next) result[i++] = x.item; return result; } @SuppressWarnings("unchecked") public <T> T[] toArray(T[] a) { if (a.length < size) a = (T[])java.lang.reflect.Array.newInstance( a.getClass().getComponentType(), size); int i = 0 ; Object[] result = a; for (Node<E> x = first; x != null ; x = x.next) result[i++] = x.item; if (a.length > size) a[size] = null ; return a; } private static final long serialVersionUID = 876323262645176354L ; 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); } @SuppressWarnings("unchecked") private void readObject (java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); int size = s.readInt(); for (int i = 0 ; i < size; i++) linkLast((E)s.readObject()); } @Override public Spliterator<E> spliterator () { return new LLSpliterator <E>(this , -1 , 0 ); } static final class LLSpliterator <E> implements Spliterator <E> { static final int BATCH_UNIT = 1 << 10 ; static final int MAX_BATCH = 1 << 25 ; final LinkedList<E> list; Node<E> current; int est; int expectedModCount; int batch; LLSpliterator(LinkedList<E> list, int est, int expectedModCount) { this .list = list; this .est = est; this .expectedModCount = expectedModCount; } final int getEst () { int s; final LinkedList<E> lst; if ((s = est) < 0 ) { if ((lst = list) == null ) s = est = 0 ; else { expectedModCount = lst.modCount; current = lst.first; s = est = lst.size; } } return s; } public long estimateSize () { return (long ) getEst(); } public Spliterator<E> trySplit () { Node<E> p; int s = getEst(); if (s > 1 && (p = current) != null ) { int n = batch + BATCH_UNIT; if (n > s) n = s; if (n > MAX_BATCH) n = MAX_BATCH; Object[] a = new Object [n]; int j = 0 ; do { a[j++] = p.item; } while ((p = p.next) != null && j < n); current = p; batch = j; est = s - j; return Spliterators.spliterator(a, 0 , j, Spliterator.ORDERED); } return null ; } public void forEachRemaining (Consumer<? super E> action) { Node<E> p; int n; if (action == null ) throw new NullPointerException (); if ((n = getEst()) > 0 && (p = current) != null ) { current = null ; est = 0 ; do { E e = p.item; p = p.next; action.accept(e); } while (p != null && --n > 0 ); } if (list.modCount != expectedModCount) throw new ConcurrentModificationException (); } public boolean tryAdvance (Consumer<? super E> action) { Node<E> p; if (action == null ) throw new NullPointerException (); if (getEst() > 0 && (p = current) != null ) { --est; E e = p.item; current = p.next; action.accept(e); if (list.modCount != expectedModCount) throw new ConcurrentModificationException (); return true ; } return false ; } public int characteristics () { return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED; } } }