{"post":{"slug":"linked-lists-explained-2026","title":"Linked lists explained in 2026: when they help and when to skip them","excerpt":"Linked lists are a CS classic that rarely belong in modern application code. Here is when they genuinely help, how they work, and why arrays usually win.","category":"code","date":"March 2, 2026","author":"ByteLedger Team","content":"<span class=\"dropcap\">L</span>inked lists appear in every data structures textbook and almost every coding interview, yet they appear in production application code surprisingly rarely. That gap exists for a reason: arrays are faster in practice for most access patterns. But linked lists are genuinely the right tool in specific situations — LRU caches, undo stacks, OS schedulers — and knowing *why* makes you a better engineer.\n\n## What changed in 2026\n\n- **LLM code assistants still suggest linked lists incorrectly.** AI completions often propose linked-list solutions for problems where `Vec`, `ArrayList`, or `deque` would be faster and simpler. Recognising the mismatch matters.\n- **Rust's `std::collections::LinkedList` added a cursor API.** The long-awaited stable cursor interface allows O(1) insert and delete at an arbitrary position without unsafe code. This makes Rust linked lists practically usable.\n- **Cache-friendly alternatives matured.** Tiered arrays, slot maps, and arena allocators give linked-list-style flexibility with cache-friendly memory layout, and are now available in most ecosystems via crates/packages.\n\n## How a linked list works\n\nEach **node** holds a value and a pointer to the next node (singly linked) or pointers to both neighbors (doubly linked).\n\n```python\nfrom dataclasses import dataclass\nfrom typing import Optional\n\n@dataclass\nclass Node:\n    value: int\n    next: Optional[\"Node\"] = None\n\n# Build: 1 → 2 → 3 → None\nhead = Node(1, Node(2, Node(3)))\n```\n\nThere is no underlying array. Nodes live wherever the allocator places them — which may be spread across heap memory.\n\n## Singly vs doubly linked\n\n| Feature | Singly linked | Doubly linked |\n|---|---|---|\n| Memory per node | value + 1 pointer | value + 2 pointers |\n| Traverse forward | O(n) | O(n) |\n| Traverse backward | Not possible | O(n) |\n| Delete at known node | O(1) with prev pointer | O(1) — has both neighbors |\n| Use cases | Stacks, forward-only queues | LRU cache, deque, undo/redo |\n\nMost production use cases need doubly linked lists. Singly linked lists are useful for purely stack-like or single-pass operations.\n\n## Time complexity\n\n| Operation | Linked list | Array/Vec |\n|---|---|---|\n| Access by index | O(n) | O(1) |\n| Insert at head | O(1) | O(n) — shift everything |\n| Insert at tail | O(1) with tail pointer | O(1) amortised |\n| Insert at known node | O(1) | O(n) — shift |\n| Delete at known node | O(1) | O(n) — shift |\n| Search | O(n) | O(n) unsorted / O(log n) sorted |\n\nLinked lists win when you already hold a pointer to the target node and need to insert or delete. If you have to *find* the node first, the O(n) search negates the O(1) modification advantage.\n\n## The LRU cache pattern\n\nThe canonical real-world linked list application: a Least Recently Used (LRU) cache using a doubly linked list + hash map.\n\n```python\nfrom collections import OrderedDict  # CPython: doubly linked list + dict\n\nclass LRUCache:\n    def __init__(self, capacity: int):\n        self.cap = capacity\n        self.cache = OrderedDict()\n\n    def get(self, key: int) -> int:\n        if key not in self.cache:\n            return -1\n        self.cache.move_to_end(key)   # O(1): doubly linked pointer swap\n        return self.cache[key]\n\n    def put(self, key: int, value: int) -> None:\n        if key in self.cache:\n            self.cache.move_to_end(key)\n        self.cache[key] = value\n        if len(self.cache) > self.cap:\n            self.cache.popitem(last=False)  # O(1): evict LRU head\n```\n\n`OrderedDict` is internally a doubly linked list married to a hash map. `move_to_end` is an O(1) pointer re-link, not an O(n) shift.\n\n## Cache locality: why arrays win in benchmarks\n\nProcessor caches work by loading contiguous memory into cache lines (~64 bytes). An array stores elements side-by-side; iterating it is cache-friendly. Linked list nodes are scattered in heap memory; every `next` pointer dereference is a potential cache miss.\n\nIn practice, iterating 1 M integers:\n- **Array/Vec:** ~2–5 ms (in-cache)\n- **Linked list:** ~20–100 ms (cache misses dominate)\n\nThis gap exists even though both are O(n). The constant factor matters when n is large.\n\n## How to pick\n\n1. **Do you need O(1) insert/delete at an arbitrary position and you hold a pointer to that position?** → Linked list.\n2. **Is this an LRU cache, undo stack, or ordered queue with frequent head/tail operations?** → Doubly linked list (or `deque`).\n3. **Do you need random access by index?** → Array.\n4. **Are you iterating sequentially and performance matters?** → Array (cache locality).\n5. **Is the language Rust and you need interior mutability?** → Consider a slot map or arena allocator for cache-friendly nodes.\n\n## Common mistakes\n\n**Losing the head pointer.** If you re-assign `head` inside a loop without saving the original, you lose the list. Always keep a reference to the head.\n\n**Forgetting the tail pointer.** Appending to a singly linked list without a tail pointer is O(n). Keep a `tail` reference for O(1) append.\n\n**Implementing linked lists in garbage-collected languages for performance.** The GC must scan every node pointer, increasing GC pause time. Arrays are better for GC-heavy runtimes.\n\n**Circular list without a sentinel.** Circular linked lists are easy to infinite-loop. Use a dummy sentinel node or Floyd's cycle detection during development.\n\n## What to skip\n\n- **Hand-rolled linked lists in Python, JavaScript, or Java for general use** — `deque`, arrays, and built-in queues are faster and safer.\n- **Linked lists for sorted data** — you lose binary search. Use a balanced BST, skip list, or sorted array. See [Binary search explained in 2026](/blog/binary-search-explained-2026).\n- **Singly linked lists where you need deletion** — without a previous-node pointer, deletion requires traversal. Use doubly linked or a different structure.\n\n## FAQ\n\n**Why do coding interviews focus so much on linked lists?**\nThey test pointer manipulation, null handling, and in-place algorithms — skills that transfer to tree and graph problems. The structure is simple enough to implement on a whiteboard.\n\n**Is `std::list` in C++ a linked list?**\nYes, it is a doubly linked list. `std::deque` and `std::vector` are usually faster for most uses. `std::list` is appropriate when you need stable iterators through frequent insertions/deletions.\n\n**What is a skip list?**\nA probabilistic data structure that layers multiple linked lists at different \"express lane\" intervals, achieving O(log n) search. Redis uses skip lists for sorted sets.\n\n**Can I reverse a linked list in place?**\nYes — iterate once, swapping `next` pointers. O(n) time, O(1) space. It is a classic interview problem that tests pointer handling.\n\n## Where to go next\n\n- [Hash tables explained in 2026](/blog/hash-tables-explained-2026)\n- [Big-O notation explained in 2026](/blog/big-o-notation-explained-2026)\n- [Binary search explained in 2026](/blog/binary-search-explained-2026)","hue":313,"metaTitle":"Linked Lists Explained 2026: Nodes, Pointers, and Trade-offs","metaDescription":"Complete 2026 guide to linked lists: singly vs doubly linked, time complexity, cache effects, real use cases like LRU caches, and when arrays beat them.","keywords":["linked lists explained 2026","singly vs doubly linked list","linked list time complexity","linked list vs array 2026","lru cache linked list","linked list use cases","how linked lists work","linked list interview prep 2026","cache locality linked list","pointer data structures"],"tldr":["**Linked lists allow O(1) insert and delete at a known node** — no shifting; perfect for LRU caches and queues where you hold a pointer to the target.","**Random access is O(n)** — to get element k you must walk from the head; arrays are O(1) by index.","**Cache locality kills real-world linked list performance** — nodes scattered in heap memory cause cache misses that make a linked list 5–20× slower than an array in practice.","**Doubly linked lists are behind most LRU cache implementations** — combined with a hash map they give O(1) get, put, and eviction.","**Skip:** replacing an `ArrayList` or `Vec` with a linked list hoping for speed — benchmark first; the array almost always wins."],"readTime":"5 min read","wordCount":1073},"related":[{"slug":"what-is-a-linked-list-2026","title":"What is a linked list in 2026? A clear, practical guide","excerpt":"A linked list stores items as a chain of nodes, each pointing to the next. This guide explains how it works, how it differs from an array, and when it is actually the right choice.","category":"code","date":"May 19, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":96},{"slug":"arrays-vs-linked-lists-2026","title":"Arrays vs Linked Lists in 2026: Which One Should You Use","excerpt":"Arrays and linked lists solve the same problem with opposite tradeoffs. Here is a scenario-by-scenario guide to picking the right one in 2026, from cache locality to insertion cost.","category":"code","date":"July 9, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":280},{"slug":"how-to-learn-data-structures-2026","title":"How to learn data structures in 2026: a practical roadmap that actually works","excerpt":"Data structures are the vocabulary of every coding interview and production system. Here is the 2026 roadmap to learn them efficiently and durably.","category":"code","date":"March 31, 2026","author":"ByteLedger Team","readTime":"5 min read","hue":307}],"sameCategory":[{"slug":"bazel-build-explained-2026","title":"Bazel Explained: What Hermetic Builds Actually Cost","excerpt":"Bazel delivers reproducible, precisely cached builds and demands that you describe everything. Here is what you get, what it costs, and who should say no.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":145},{"slug":"graphql-federation-2026","title":"GraphQL Federation: One Graph, Many Teams","excerpt":"Federation composes independently-owned subgraphs into a single API. Here is what it solves, the operational cost it adds, and when a simpler gateway is enough.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":300},{"slug":"hermetic-builds-explained-2026","title":"Hermetic Builds Explained: Same Inputs, Same Output","excerpt":"A hermetic build depends only on declared inputs. Here is what that property gives you, the common leaks that break it, and how to move toward it incrementally.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":130},{"slug":"monorepo-tooling-compared-2026","title":"Monorepo Tooling Compared: What You Actually Need","excerpt":"Monorepo tools solve task orchestration and caching, not repository layout. Here is what each tier provides and how to tell when you have outgrown the simple option.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":150}],"newer":{"slug":"lease-vs-buy-a-car-2026","title":"Lease vs buy a car in 2026: the honest numbers breakdown","excerpt":"Leasing and buying both have legitimate use cases, but the math favors one clearly for most drivers. Here is the 2026 framework for making the right call.","category":"finance","date":"March 2, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":282},"older":{"slug":"loss-aversion-explained-2026","title":"Loss aversion explained in 2026: why losses hurt twice as much as gains feel good","excerpt":"Loss aversion makes you keep bad investments, avoid bold decisions, and misread risk. Here is how the bias works and how to correct for it in 2026.","category":"guides","date":"March 2, 2026","author":"ByteLedger Team","readTime":"5 min read","hue":155}}