LinkedList<T>
LinkedList<T> (std::collections::LinkedList<T>) is a doubly linked list of individually heap-allocated nodes, each storing an element T alongside prev and next pointers.
Memory Layout
Stack (`LinkedList<T>`)
+-----------+-----------+-------+
| head | tail | len=3 |
+-----+-----+-----+-----+-------+
| |
v +-----------------------------------+
+---------------+ +---------------+ +-------v-------+
| prev: None | ---> | prev: Some | ---> | prev: Some |
| next: Some | <--- | next: Some | <--- | next: None |
| elem: 10 | | elem: 20 | | elem: 30 |
+---------------+ +---------------+ +---------------+
Every insertion into a LinkedList<T> performs a separate heap allocation for the node and stores two extra machine words (prev and next pointers, 16 bytes on 64-bit) alongside T.
Operations and Complexity
| Operation | Method | Time Complexity |
|---|---|---|
| Push / pop at front | list.push_front(x) / list.pop_front() |
$O(1)$ |
| Push / pop at back | list.push_back(x) / list.pop_back() |
$O(1)$ |
Move all nodes from other onto the end of self |
list.append(&mut other) |
$O(1)$ (relinks tail/head pointers) |
Split list at index at |
list.split_off(at) |
$O(\min(\text{at}, n - \text{at}))$ traversal, $O(1)$ relink |
| Random access by index | Not supported (list.iter().nth(i)) |
$O(n)$ pointer chase |
O(1) List Concatenation with append() and Splitting with split_off()
The primary operation where LinkedList<T> has an asymptotic advantage over Vec<T> and VecDeque<T> is list.append(&mut other), which concatenates two lists of arbitrary length in $O(1)$ time by updating four pointers without copying or reallocating any elements:
use std::collections::LinkedList;
fn main() {
let mut first = LinkedList::from(["a", "b"]);
let mut second = LinkedList::from(["c", "d", "e"]);
// Relinks `second` onto the tail of `first` in O(1) time; `second` becomes empty:
first.append(&mut second);
println!("After append: first={first:?}, second={second:?}");
// Splits `first` at index 3, returning elements from index 3 onward:
let tail = first.split_off(3);
println!("After split_off: first={first:?}, tail={tail:?}");
}
After append: first=["a", "b", "c", "d", "e"], second=[]
After split_off: first=["a", "b", "c"], tail=["d", "e"]
Why Vec<T> and VecDeque<T> Are Almost Always Preferred
Even when inserting or removing elements in the middle of a sequence of a few thousand items, Vec<T> and VecDeque<T> typically outperform LinkedList<T> on modern CPUs because:
- Reaching the middle of a
LinkedList<T>requires $O(n)$ sequential pointer chasing across non-contiguous heap addresses (causing CPU cache misses on each node), whereasVec<T>shifts contiguous cache lines with hardware-prefetched SIMDmemmove. LinkedList<T>allocates and frees a heap node on every singlepushandpop, whereasVec<T>andVecDeque<T>amortize allocations across geometric growth.