0
0
C Sharp (C#)programming~15 mins

List generic collection in C Sharp (C#) - Deep Dive

Choose your learning style9 modes available
Overview - List generic collection
What is it?
A List generic collection in C# is a flexible container that holds a sequence of items of the same type. It allows you to add, remove, and access elements by their position easily. Unlike arrays, Lists can grow or shrink dynamically as you work with them. This makes Lists very useful for managing groups of data when the size can change.
Why it matters
Without Lists, programmers would have to use fixed-size arrays or more complex structures to manage collections of items, which can be inefficient and error-prone. Lists solve the problem of needing a simple, dynamic way to store and manipulate groups of objects. This flexibility is essential in almost every software application, from games to business software, where data changes over time.
Where it fits
Before learning Lists, you should understand basic C# types, variables, and arrays. After mastering Lists, you can explore other collection types like Dictionaries, Queues, and LinkedLists, and learn about LINQ for powerful data queries.
Mental Model
Core Idea
A List is like a resizable row of boxes where you can store items of the same kind, add more boxes anytime, or remove some, all while keeping track of their order.
Think of it like...
Imagine a row of mailboxes where each mailbox holds a letter. You can add more mailboxes if you get more letters, or remove empty mailboxes if you have fewer letters. You always know which mailbox holds which letter by its position in the row.
List<T> Collection
┌───────────────┐
│ Index: 0      │ → Item 1
├───────────────┤
│ Index: 1      │ → Item 2
├───────────────┤
│ Index: 2      │ → Item 3
├───────────────┤
│ ...           │
└───────────────┘
Supports:
- Add(item)
- Remove(item)
- Access by index
- Dynamic resizing
Build-Up - 7 Steps
1
FoundationUnderstanding List Basics
🤔
Concept: Introduces what a List is and how to declare it in C#.
In C#, a List is declared using the generic type List, where T is the type of items it will hold. For example, List holds integers. You can create an empty List and add items later. Example: using System.Collections.Generic; List numbers = new List(); numbers.Add(10); numbers.Add(20); Console.WriteLine(numbers[0]); // Outputs 10
Result
The program creates a List of integers, adds two numbers, and prints the first number, which is 10.
Understanding how to declare and add items to a List is the foundation for using dynamic collections in C#.
2
FoundationAccessing and Modifying List Items
🤔
Concept: Shows how to get, change, and remove items by their position or value.
You can access items in a List by their index using square brackets, like numbers[0]. You can also change an item by assigning a new value to that index. To remove items, you can use Remove(item) to remove by value or RemoveAt(index) to remove by position. Example: List fruits = new List { "Apple", "Banana", "Cherry" }; string firstFruit = fruits[0]; // Apple fruits[1] = "Blueberry"; // Change Banana to Blueberry fruits.Remove("Cherry"); // Remove Cherry Console.WriteLine(string.Join(", ", fruits)); // Apple, Blueberry
Result
The List changes from Apple, Banana, Cherry to Apple, Blueberry after modification and removal.
Knowing how to access and modify List items by index or value lets you manage your data precisely and flexibly.
3
IntermediateList Capacity and Count Explained
🤔Before reading on: Do you think List's Count and Capacity are always the same? Commit to your answer.
Concept: Introduces the difference between how many items a List holds (Count) and how much space it has reserved (Capacity).
A List has two important properties: Count and Capacity. Count is how many items are currently in the List. Capacity is how many items the List can hold before it needs to resize its internal storage. When you add items beyond Capacity, the List automatically increases its Capacity to fit more items. Example: List nums = new List(); Console.WriteLine(nums.Count); // 0 Console.WriteLine(nums.Capacity); // Usually 0 or small nums.Add(1); Console.WriteLine(nums.Count); // 1 Console.WriteLine(nums.Capacity); // May increase to 4 or 8
Result
Count shows the actual number of items; Capacity shows reserved space, which grows automatically.
Understanding Count vs Capacity helps you write efficient code by avoiding unnecessary resizing, which can slow down your program.
4
IntermediateUsing List with Different Data Types
🤔Before reading on: Can a List hold items of different types at the same time? Commit to your answer.
Concept: Explains that Lists are strongly typed and hold only one type of item, but you can use object type or inheritance to store different types.
Lists are generic, meaning they hold items of one specific type. For example, List holds only strings. You cannot mix types like strings and integers in the same List. However, if you use List, you can store any type because all types inherit from object. Example: List mixed = new List(); mixed.Add(1); mixed.Add("hello"); mixed.Add(3.14); foreach(var item in mixed) { Console.WriteLine(item); }
Result
The List prints 1, hello, and 3.14, showing it holds different types as objects.
Knowing Lists are strongly typed prevents type errors and helps you design your data structures correctly.
5
IntermediateIterating Over List Items
🤔
Concept: Shows how to loop through all items in a List using foreach and for loops.
To process each item in a List, you can use a foreach loop, which automatically goes through every element. Alternatively, a for loop lets you use the index to access items. Example: List colors = new List { "Red", "Green", "Blue" }; // Using foreach foreach (string color in colors) { Console.WriteLine(color); } // Using for for (int i = 0; i < colors.Count; i++) { Console.WriteLine($"Color at {i}: {colors[i]}"); }
Result
The program prints each color name, showing two ways to access List items.
Mastering iteration is key to working with Lists effectively in real programs.
6
AdvancedPerformance and Internal Resizing
🤔Before reading on: Do you think adding items to a List always takes the same time? Commit to your answer.
Concept: Explains how List resizes internally and how this affects performance when adding many items.
When you add items to a List, it usually adds them quickly. But when the List reaches its Capacity, it must resize its internal array to a bigger size, which takes extra time because it copies all items to the new space. This resizing happens automatically and usually doubles the Capacity to reduce how often it happens. Example: List bigList = new List(); for (int i = 0; i < 10000; i++) { bigList.Add(i); } // Resizing happens a few times during this loop.
Result
Adding many items is fast most of the time but slower during resizing moments.
Understanding resizing helps you optimize performance by setting Capacity in advance if you know the size.
7
ExpertThread Safety and Concurrent Access
🤔Before reading on: Is List safe to use from multiple threads at the same time? Commit to your answer.
Concept: Discusses that List is not thread-safe and how to handle concurrent access in multi-threaded programs.
List is not designed to be safe when multiple threads add or remove items at the same time. Doing so can cause data corruption or exceptions. To use Lists safely in multi-threaded environments, you can use locking (lock keyword) to protect access or use thread-safe collections like ConcurrentBag or ConcurrentQueue. Example: private readonly object _lock = new object(); List sharedList = new List(); void AddItem(int item) { lock(_lock) { sharedList.Add(item); } }
Result
Using locks prevents data corruption when multiple threads modify the List.
Knowing List's thread safety limits prevents subtle bugs in concurrent applications.
Under the Hood
Internally, List uses an array to store its items. When you add items, it places them in the array sequentially. If the array is full, List creates a new larger array (usually double the size), copies all existing items to it, and then adds the new item. This resizing is hidden from the user but affects performance. Accessing items by index is fast because arrays provide direct access.
Why designed this way?
List was designed to combine the speed of arrays with the flexibility of dynamic sizing. Arrays are fast but fixed size, while linked lists are flexible but slower to access by index. Using an internal array with resizing balances these trade-offs, providing fast access and reasonable flexibility. Alternatives like linked lists or other collections exist but serve different needs.
List<T> Internal Structure
┌─────────────────────────────┐
│ Internal Array (capacity N) │
│ ┌─────┬─────┬─────┬─────┐   │
│ │ 0   │ 1   │ 2   │ ... │   │
│ │item0│item1│item2│     │   │
│ └─────┴─────┴─────┴─────┘   │
│ Count = number of items      │
│ Capacity = array length      │
└─────────────────────────────┘
When full:
  ↓
Create new array with bigger size
Copy old items
Replace internal array
Add new item
Myth Busters - 4 Common Misconceptions
Quick: Does List automatically shrink its capacity when you remove items? Commit to yes or no.
Common Belief:Many believe that when you remove items from a List, its capacity shrinks automatically to save memory.
Tap to reveal reality
Reality:List does not reduce its capacity automatically when items are removed. The internal array stays the same size until you explicitly call TrimExcess() or create a new List.
Why it matters:Assuming capacity shrinks can lead to unexpected high memory use in long-running programs that remove many items.
Quick: Can you store different types in a List without casting? Commit to yes or no.
Common Belief:Some think you can add different types of objects to a List without any casting or errors.
Tap to reveal reality
Reality:List is strongly typed and only accepts items of type T or derived types. Adding different types requires using List or casting.
Why it matters:Misunderstanding this causes compile-time errors or runtime exceptions.
Quick: Is List thread-safe for concurrent writes? Commit to yes or no.
Common Belief:Many assume List is safe to use from multiple threads at the same time without extra protection.
Tap to reveal reality
Reality:List is not thread-safe. Concurrent modifications can corrupt data or cause exceptions.
Why it matters:Ignoring thread safety leads to hard-to-debug crashes and data corruption in multi-threaded apps.
Quick: Does adding items to a List always take the same amount of time? Commit to yes or no.
Common Belief:Some believe adding an item to a List always takes constant time, no matter what.
Tap to reveal reality
Reality:Adding items usually takes constant time, but occasionally resizing the internal array takes longer, causing a slower operation.
Why it matters:Not knowing this can cause performance surprises in time-critical applications.
Expert Zone
1
List uses an internal array doubling strategy for resizing, which balances memory use and performance but can cause temporary spikes in memory during resizing.
2
The TrimExcess() method can reduce capacity to match Count, but overusing it can hurt performance if the List grows again soon after.
3
Using List with structs (value types) avoids extra memory allocations but requires care to avoid copying large structs unintentionally.
When NOT to use
Avoid using List when you need thread-safe concurrent access; use concurrent collections like ConcurrentBag instead. Also, if you need fast insertions or deletions in the middle of the collection, LinkedList or other specialized collections may be better. For fixed-size collections, arrays are simpler and more efficient.
Production Patterns
In real-world applications, Lists are often used to collect data dynamically, such as user inputs, database query results, or temporary buffers. Developers pre-set Capacity when the size is known to improve performance. Lists are combined with LINQ for filtering and transforming data. In multi-threaded apps, Lists are wrapped with locks or replaced by thread-safe collections.
Connections
Array
List builds on the concept of arrays by adding dynamic resizing and flexible methods.
Understanding arrays helps grasp why List uses an internal array and how indexing works efficiently.
Linked List
Linked Lists provide an alternative to List with different performance trade-offs for insertions and deletions.
Knowing Linked Lists clarifies when List is not the best choice, especially for frequent middle insertions.
Memory Management in Operating Systems
List's resizing behavior relates to how operating systems allocate and manage memory blocks dynamically.
Understanding memory allocation helps explain why resizing causes performance costs and temporary memory spikes.
Common Pitfalls
#1Assuming List capacity shrinks automatically after removing items.
Wrong approach:List numbers = new List {1,2,3,4,5}; numbers.RemoveAt(0); Console.WriteLine(numbers.Capacity); // Expecting capacity to reduce
Correct approach:List numbers = new List {1,2,3,4,5}; numbers.RemoveAt(0); numbers.TrimExcess(); Console.WriteLine(numbers.Capacity); // Capacity reduced
Root cause:Misunderstanding that capacity and count are separate and capacity does not shrink automatically.
#2Trying to add different types to a strongly typed List without casting.
Wrong approach:List numbers = new List(); numbers.Add(10); numbers.Add("twenty"); // Compile error
Correct approach:List items = new List(); items.Add(10); items.Add("twenty"); // Works fine
Root cause:Not realizing List enforces type safety at compile time.
#3Using List from multiple threads without synchronization.
Wrong approach:List shared = new List(); // Thread 1 shared.Add("A"); // Thread 2 shared.Add("B"); // May cause race condition
Correct approach:private readonly object _lock = new object(); List shared = new List(); lock(_lock) { shared.Add("A"); } lock(_lock) { shared.Add("B"); }
Root cause:Ignoring thread safety requirements of List.
Key Takeaways
List is a flexible, dynamic collection that stores items of the same type and allows easy adding, removing, and accessing by index.
It uses an internal array that resizes automatically, balancing speed and flexibility but causing occasional performance costs during resizing.
Lists are strongly typed, so all items must be of the declared type or compatible types, ensuring type safety.
List is not thread-safe; concurrent access requires synchronization or alternative thread-safe collections.
Understanding List capacity, count, and iteration methods is essential for writing efficient and correct C# programs.