List vs LinkedList in C#: When to Use Each

Use List<T> by default in C#, and choose LinkedList<T> only when you hold references to nodes and insert or remove at those nodes often. List<T> is a resizable array: index access is O(1), adding at the end is O(1) on average, and inserting or removing in the middle is O(n) because later elements shift. LinkedList<T> is a doubly linked list. Each element is a node with a Next and a Previous reference, so inserting or removing at a node you already hold is O(1). There is no indexer, and reaching a position means walking from one end, which is O(n).

In practice that makes List<T> the right choice in almost every case, including most cases that sound like linked list work. The exceptions are specific, and they are listed below.

How List<T> works

List<T> keeps its elements in one array and tracks how many slots are in use. When the array is full, Add allocates a new array of double the capacity and copies the elements across. That copy is why Add is O(1) only on average, which is called amortized constant time.

Because the elements sit next to each other in memory, list[i] is a single address calculation. Iterating a List<T> is also fast, since the processor cache loads neighboring elements together.

var numbers = new List<int> { 1, 2, 3, 4, 5 }; Console.WriteLine(numbers[2]); // 3, O(1) numbers.Add(6); // O(1) amortized numbers.Insert(0, 0); // O(n): shifts six elements right numbers.RemoveAt(3); // O(n): shifts the tail left

How LinkedList<T> works

LinkedList<T> stores each element in a LinkedListNode<T> object. A node holds the value, a reference to the next node, a reference to the previous node, and a reference to its list. The list itself holds only First, Last and Count.

There is no list[i]. LinkedList<T> implements ICollection<T> but not IList<T>, and the LINQ method ElementAt(i) walks i steps to get there. What you get instead is O(1) insertion and removal at any node whose reference you already hold.

var names = new LinkedList<string>(); names.AddLast("b"); names.AddLast("d"); names.AddFirst("a"); // a, b, d LinkedListNode<string> node = names.Find("b"); // O(n) search names.AddAfter(node, "c"); // O(1): a, b, c, d names.Remove("d"); // O(n): searches by value names.RemoveFirst(); // O(1): b, c for (var n = names.First; n != null; n = n.Next) Console.WriteLine(n.Value);

Remove("d") searches, while Remove(node) with a node reference is O(1). The speed of a linked list exists only when you skip the search.

List vs LinkedList: side by side

OperationList<T>LinkedList<T>
Access by indexO(1)Not supported; ElementAt is O(n)
Add at endO(1) amortizedO(1)
Add or remove at frontO(n)O(1)
Insert or remove in the middleO(n), shifts elementsO(1) with a node reference, O(n) to find one
Search by valueO(n)O(n)
Memory per elementThe value, plus unused capacityThe value plus three references and an object header
IterationFast, contiguous memorySlower, each node is a separate object
InterfacesIList<T>ICollection<T> only

On a 64-bit runtime a LinkedListNode<int> costs about 48 bytes, against 4 bytes per int inside a List<int>. For a million elements that is about 48 MB against 4 to 8 MB, depending on spare capacity, plus the time to allocate a million small objects.

When LinkedList<T> is the right choice

You keep node references and edit at them. The clearest example is an LRU cache, which is a cache that evicts the least recently used entry when it is full. A Dictionary<TKey, LinkedListNode<TValue>> finds the node in O(1), and Remove(node) followed by AddFirst(node) moves it to the front in O(1). A List<T> would spend O(n) on every move.

You add and remove at both ends and also from the middle. A scheduler that cancels arbitrary pending items by node reference is one case. If you only need both ends, Queue<T> and Stack<T> are array based and faster.

References must stay valid across insertions. A List<T> index becomes wrong as soon as something is inserted before it. A LinkedListNode<T> stays correct.

When List<T> is the right choice

Every other case. Indexing, sorting, binary search, slicing with GetRange, and LINQ over a large collection all run faster on an array. Even inserting in the middle is often faster on a List<T> when the collection is small. Copying a few hundred elements costs less than walking a few hundred nodes to find the position.

If you are unsure, use List<T>, measure, and switch only when a profiler shows the shifting cost.

One note for readers who know Java. Java's LinkedList implements both List and Deque, so it has get(i) and can serve as a queue. The C# type has no indexer and does not implement IList<T>, so the two are not interchangeable in a design discussion.

Key Takeaways

TAGS
Coding Interview
CONTRIBUTOR
Arslan Ahmad
Arslan Ahmad
ex-FAANG engineering manager and author or Grokking series.

GET YOUR FREE

Coding Questions Catalog

Design Gurus Newsletter - Latest from our Blog
Boost your coding skills with our essential coding questions catalog.
Take a step towards a better tech career now!
Explore Answers
What is the purpose of the `self` parameter? Why is it needed?
What is a data structure and algorithm?
What is a lazy portfolio?
What is the full form of IBM?
What Is the Wipro Interview Process Like? (Round by Round)
The reported Wipro hiring path: the Elite NTH online test, a communication check, the business discussion round, and the HR round.
Who is the highest paid on Pinterest?
Related Courses
New
Grokking the AI System Design Interview course cover
Grokking the AI System Design Interview
Learn to design AI systems the way interviewers expect: classic ML products, LLM and RAG architectures, and agentic systems, all through the lens of the system design interview.
4.6
(3,192 learners)
Discounted price for Your Region

$99

Grokking the Coding Interview: Patterns for Coding Questions course cover
Grokking the Coding Interview: Patterns for Coding Questions
The 24 essential patterns behind every coding interview question. Available in Java, Python, JavaScript, C++, C#, and Go. The most comprehensive coding interview course with 543 lessons. A smarter alternative to grinding LeetCode.
4.6
Discounted price for Your Region

$197

Grokking Modern AI Fundamentals course cover
Grokking Modern AI Fundamentals
Master the fundamentals of AI today to lead the tech revolution of tomorrow.
4.1
Discounted price for Your Region

$72

Design Gurus logo
One-Stop Portal For Tech Interviews.
About Us
Our Team
Careers
Partners
Contact Us
Become Affiliate
Copyright © 2026 Design Gurus, LLC. All rights reserved.