LinkedList源码解析!
发表于更新于
编程语言JavaLinkedList源码解析!
月伴飞鱼LinkedList 适用于要求有顺序、并且会按照顺序进行迭代的场景,底层数据结构是一个双向链表:

链表每个节点我们叫做 Node,Node 有 prev 属性,代表前一个节点的位置,next 属性,代表后一个节点的位置。
first 是双向链表的头节点,它的前一个节点是 null。
last 是双向链表的尾节点,它的后一个节点是 null。
当链表中没有数据时,first 和 last 是同一个节点,前后指向都是 null。
因为是个双向链表,只要机器内存足够强大,是没有大小限制的。
1 2
| transient Node<E> first; transient Node<E> last;
|
1 2 3 4 5 6 7 8 9 10 11 12
| 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; } }
|
追加(新增)
追加节点时,add 方法默认是从尾部开始追加,addFirst 方法是从头部开始追加。
尾部追加节点比较简单,只需要简单地把指向位置修改下即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public void addLast(E e) { linkLast(e); } 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++; }
|
节点删除
节点删除的方式和追加类似,可以选择从头部删除。
也可以选择从尾部删除,删除操作会把节点的值,前后指向节点都置为 null,帮助 GC 进行回收。
链表结构的节点新增、删除都非常简单,仅仅把前后节点的指向修改下就好了,所以 LinkedList 新增和删除速度很快。
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
| 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; first = next; if (next == null) last = null; else next.prev = null; size--; modCount++; return element; }
|
节点查询
链表查询某一个节点是比较慢的,需要挨个循环查找。
LinkedList 并没有采用从头循环到尾的做法,而是采取了简单二分法,首先看看 index 是在链表的前半部分,还是后半部分。
如果是前半部分,就从头开始寻找,反之亦然。通过这种方式,使循环的次数至少降低了一半,提高了查找的性能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 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; } }
|