Beyond the Interview: Data Structures (#2 Linked Lists)

About This Series
We’ve all been there—grinding LeetCode, memorizing tree traversals, and pretending to care about Red-Black Trees because some Big Tech interviewer insists they’re “fundamental.”
But here’s the reality: 99% of the time, you’re just using Lists, Arrays, and HashMaps—and that’s fine.
Read more
So why bother with the rest? Because sometimes the default isn’t the best choice.
- Maybe a different data structure completely revolutionizes your system.
- Maybe it opens up new possibilities you hadn’t considered.
- Maybe it’s the key to removing that one persistent bottleneck that refuses to budge no matter how many CPU cores you throw at it.
Some data structures solve real-world problems, while others exist purely to torture CS students. In this series, we’ll cut through the nonsense—breaking down:
✅ When data structures actually matter
✅ What trade-offs they introduce
✅ Where they fit into practical software development
No fluff. No pointless academic exercises. Just real-world insights, benchmarks, and maybe a few laughs along the way.
Bonus: In your next tech interview, you’ll be able to talk about the pros and cons of Bloom Filters and Suffix Trees as easily as reversing an array—and that will make you stand out.
I mention some internal workings related to performance (stack vs heap, garbage collection, etc). If you’re not up to speed, check out Dig Deeper: Memory Management in .NET].
Explore This Series
Arrays & Lists | Linked Lists
📌 Linked Lists in C# - Why You Should (Usually) Avoid Them
Most developers rarely use LinkedList<T>
in real-world .NET applications—and for good reason. While linked lists sound useful in theory, they often perform worse than List<T>
due to poor cache locality and higher memory overhead.
But are linked lists completely useless? Not quite. Let’s break it down.
🔹 Singly vs. Doubly Linked Lists in .NET
By default, LinkedList<T>
in .NET is doubly linked, meaning:
✅ Each node has a reference to both its next and previous node.
✅ Backward traversal is easy.
❌ Doubles the memory overhead (extra pointer per node).
📌 Example: LinkedList<T>
in .NET
LinkedList<int> linkedList = new(); // Doubly linked list
linkedList.AddLast(1);
linkedList.AddLast(2);
linkedList.AddLast(3);
👉 Each node in this list has pointers to both its next and previous node.
🔹 When is a Singly Linked List Better?
A singly linked list only has a forward reference, meaning it:
✅ Uses half the memory compared to a doubly linked list.
✅ Has fewer pointer updates when inserting/removing elements.
❌ Cannot traverse backwards.
⚠️ Important: In .NET, LinkedList<T>
is always doubly linked. If you need a singly linked list, you’ll have to implement it yourself.
🔹 Performance Pitfalls: Why LinkedList<T>
is (Almost) Never the Right Choice
Linked lists sound useful—until you actually try to use them.
Here’s why LinkedList<T>
performs worse than List<T>
in most cases:
1️⃣ Pointer Chasing Kills CPU Performance
❌ Linked lists store elements scattered across memory (non-contiguous).
❌ Every node access requires an extra pointer dereference, leading to cache misses and slower performance.
✅ Arrays (T[]
) and List<T>
store data contiguously, making CPU access faster.
Imagine needing directions to your own fridge every time you wanted a snack! At the end of the day, pointer chasing is fun for dogs, less fun for your CPU. 🐶
2️⃣ Iteration is Slower (O(n)
)
❌ Accessing an element in LinkedList<T>
requires traversing the list node-by-node (O(n)
).
✅ List<T>
provides instant O(1)
index-based access.
📌 Benchmark: Iterating Over List<T>
vs. LinkedList<T>
Method | Mean | Allocated |
---|---|---|
ListIteration |
2.5ms | 400 KB |
LinkedListIter |
15.2ms | 1.2 MB |
ArrayIteration |
1.1ms | 0 KB |
👉 Conclusion:
🚫 Avoid LinkedList<T>
unless:
- 🔹 Insertions & deletions vastly outnumber lookups.
- 🔹 You don’t need indexed access.
Otherwise, List<T>
is almost always the better choice.
🔹 When Does LinkedList<T>
Make Sense?
Despite its downsides, there are still some cases where a linked list shines:
✅ Frequent Insertions/Deletions at Arbitrary Positions
📌 Inserting into a List<T>
is O(n) (due to shifting elements).
📌 LinkedList<T>
allows O(1) insertions/removals if you have a reference to the node.
🔹 Example: A job scheduler that frequently reorders tasks or an ordered log of events where insertions happen frequently, but lookups are rare.
✅ Large Datasets Where Allocations Are Spread Out
📌 If your dataset changes frequently and grows unpredictably, LinkedList<T>
prevents large reallocation costs.
🔹 Example: Real-time data processing (e.g., message queues).
✅ Undo/Redo Stacks
📌 Operations that need a history of changes (e.g., text editor undo system) are easier with linked lists.
🔥 Final Verdict: Should you use LinkedList<T>
?
Probably not. But now you know when it makes sense—and how to explain it in excruciating detail at your next interview while they silently regret asking the question. 😎