NOTE
5.10 LinkedList
What LinkedList is, usage, and source analysis of its doubly linked list, add/remove operations, and node traversal.
This is a historical learning note and may contain outdated or incomplete understanding.
1. What Is It?
A sequential list implemented underneath with a doubly linked list.
It is ordered and allows duplicates.
2. How to Use It
public class LinkedListTest
{
public static void main(String[] args)
{
LinkedList<String> list = new LinkedList<>();
list.add("1");
list.add("2");
list.addFirst("3");
list.addLast("4");
System.out.println(list);
list.remove(0);
list.remove("2");
list.remove();
list.removeFirst();
list.removeLast();
}
}
3. Implementation Analysis
3.1. UML

It can be seen that LinkedList is a List, a double-ended queue, serializable, and cloneable.
3.2. Constructor
It consists of a head node, tail node, and length.
public class LinkedList<E>
extends AbstractSequentialList<E>//Provides a skeletal implementation of List.
implements List<E>/*List interface*/, Deque<E>/*double-ended queue*/, Cloneable, java.io.Serializable
{
// Fields
transient int size = 0;//Length
transient Node<E> first;//Head node
transient Node<E> last;//Tail node
// Constructor
public LinkedList() {
}
}
3.2.1. Queue Node
private static class Node<E> {
E item;//Data
Node<E> next;//Next pointer
Node<E> prev;//Previous pointer
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
The structure is shown below:

3.3. add Method
- O(1)
public boolean add(E e) {
// Call linkLast.
linkLast(e);
return true;
}
3.3.1. Insert at the Tail of the List
- linkLast
void linkLast(E e) {
// Save the tail node.
final Node<E> l = last;
// Construct a new node whose prev points to the old tail.
final Node<E> newNode = new Node<>(l, e, null);
// Update last to the new node.
last = newNode;
// If the old tail was null, this is the first node.
if (l == null)
first = newNode;
else
// Otherwise update the old tail's next pointer.
l.next = newNode;
size++;
modCount++;
}
3.3.2. Construct a New Node [prev Points to Tail, next Is null]
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
3.3.3. Update the Tail Node
if (l == null)
first = newNode;
else
l.next = newNode;
3.3.4. Update size
size++;
3.4. addLast Method
- O(1)
public void addLast(E e) {
// Call linkLast.
linkLast(e);
}
- linkLast
See the add method.
3.5. addFirst Method
- O(1)
public void addFirst(E e) {
// Simply call linkFirst.
linkFirst(e);
}
3.5.1. Insert a Node at the Head
- linkFirst
private void linkFirst(E e) {
// Save the old head node.
final Node<E> f = first;
// Create a new node whose next points to the old head.
final Node<E> newNode = new Node<>(null, e, f);
// Update first to the new node.
first = newNode;
// If the old head was null, this is the first element,
// so update last to the new node.
if (f == null)
last = newNode;
else
// Otherwise update the old head's prev pointer.
f.prev = newNode;
size++;
modCount++;
}
3.5.2. Construct a New Node [prev Is null, next Points to Head]
final Node<E> f = first;
final Node<E> newNode = new Node<>(null, e, f);
3.5.3. Update the Head Node
if (f == null)
last = newNode;
else
f.prev = newNode;
3.5.4. Update size
size++;
3.6. remove Method [Delete by Index]
- O(N)
- Modify the
nextandprevpointers of the nodes before and after the target node.
public E remove(int index) {
// Check whether the index is out of bounds.
checkElementIndex(index);
// Find the corresponding node by index and delete it.
return unlink(node(index));
}
3.6.1. Traverse to Find the Node at This Position
- node
Node<E> node(int index) {
// assert isElementIndex(index);
// Index is in the left half of the list.
if (index < (size >> 1)) {
// Search rightward from the head.
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
// Index is in the right half.
} else {
// Search leftward from the tail.
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
3.6.2. Update the Pointers of the Nodes Before and After It
E unlink(Node<E> x) {
// assert x != null;
// Save prev, next, and item of the current node.
final E element = x.item;
final Node<E> next = x.next;
final Node<E> prev = x.prev;
// Update prev.
if (prev == null) {
// No previous node means x is the head; point first to x.next.
first = next;
} else {
// Previous node's next points to current node's next.
prev.next = next;
// help GC
x.prev = null;
}
// Update next.
if (next == null) {
// No next node means x is the tail; point last to x.prev.
last = prev;
} else {
// Next node's prev points to current node's prev.
next.prev = prev;
// help GC
x.next = null;
}
// help GC
x.item = null;
size--;//Update size.
modCount++;
return element;
}
3.7. remove Method [Delete by Element]
- O(N)
public boolean remove(Object o) {
// == null
if (o == null) {
// Traverse to find the node.
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null) {
unlink(x);
return true;
}
}
// equals
} else {
// Traverse to find the node.
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
}
3.7.1. Traverse to Find the Element
for (Node<E> x = first; x != null; x = x.next)
{
//...
}
3.7.2. Update the Pointers of the Nodes Before and After It
The pointer-update logic is the same as unlink above.
3.8. remove Method [No Arguments]
- O(1)
public E remove() {
// Simply call removeFirst.
return removeFirst();
}
3.8.1. Delete the Head Node
- removeFirst
public E removeFirst() {
// Head node.
final Node<E> f = first;
if (f == null)
// Throw if null.
throw new NoSuchElementException();
// Call unlinkFirst to remove the head.
return unlinkFirst(f);
}
3.8.2. Make the Old Head’s next Node the New Head
- unlinkFirst
private E unlinkFirst(Node<E> f) {
// assert f == first && f != null;
// Save item and next.
final E element = f.item;
final Node<E> next = f.next;
// help GC
f.item = null;
f.next = null;
// first directly points to the old head's next.
first = next;
// If the head's next is null, there was only one node.
if (next == null)
// Update last to null.
last = null;
else
// Otherwise clear the new head's prev pointer.
next.prev = null;
size--;
modCount++;
return element;
}
3.9. removeLast Method
- O(1)
public E removeLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return unlinkLast(l);
}
3.9.1. Make the Old Tail’s prev Node the New Tail
- unlinkLast
private E unlinkLast(Node<E> l) {
// assert l == last && l != null;
// Save the tail's item and prev.
final E element = l.item;
final Node<E> prev = l.prev;
// help GC
l.item = null;
l.prev = null;
// Update last to the old tail's prev.
last = prev;
// Only one element.
if (prev == null)
first = null;
else
prev.next = null;
size--;
modCount++;
return element;
}
Discussion
Sign in with GitHub to comment. Discussions are stored as GitHub Issues.View on GitHub