Skip to main content

Command Palette

Search for a command to run...

C# Collection Types for Beginners

Published
4 min read
C# Collection Types for Beginners
L

Heya! I'm Lou. I love coding and trying out new technology. I've been interested in a lot of different tech fields for 5 years now. I like to blog about stuff I'm currently learning about or about topics I hear / speak about on other social media platforms.

I'm also Microsoft MVP.

Want to hear more from me? Follow me or check me out on Twitter.

Hi lovely readers,

Collections help you store and manage groups of items, like names in a list or values in a lookup table. Choosing the right collection can save you time, prevent bugs, and make everything way faster. When I got started with coding it was a big challenge for me which collection type I should use to get the best result. So I hope that this blog post will help you to clear some stuff up.

Let’s get started!


1. Why Collections Matter

When you start coding, you often use single variables. But real apps need to handle many items: user inputs, search results, configuration values, and more. Collections let you group these items together so you can:

  • Store many values in one object

  • Iterate through items easily (e.g., with foreach)

  • Search, sort, and filter data

  • Share data between methods and classes

This is backend development 101, so I hope you already know what lists and array do to a certain extend. However, having a solid grasp of collections saves you so much time and speed so be sure you know how all collection types work.


2. Non-Generic vs. Generic Collections

Non-Generic Collections

Older collection types (like ArrayList and Hashtable) store items as object. When you retrieve an item, you must cast it:

var list = new ArrayList();
list.Add("hello");
string s = (string)list[0];  // explicit cast

Drawbacks:

  • Runtime errors if the cast is wrong

  • Boxing/unboxing slows performance

  • Less clear code, since item types are not explicit

Generic Collections

Generics let you specify the item type up front (List<T>, Dictionary<TKey, TValue>, HashSet<T>). For example:

var numbers = new List<int>();
numbers.Add(10);
int x = numbers[0];  // no cast needed

Benefits:

  1. Type safety: Compiler checks item types

  2. Better performance: No boxing/unboxing

  3. Clearer code: You see the type at a glance

Use generics in almost all cases. Only use non-generic collections for legacy code.


3. Main Collection Types in .NET

Arrays

  • What? Fixed-size, zero-based collections of a single type.

  • When? You know the exact number of items.

string[] names = new string[3];
names[0] = "Alice";
names[1] = "Bob";
names[2] = "Carol";

foreach (var name in names)
    Console.WriteLine(name);

Lists (List<T>)

  • What? Resizable arrays—grow or shrink automatically.

  • When? You need dynamic collections.

var list = new List<int> { 1, 2, 3 };
list.Add(4);
list.RemoveAt(0);

foreach (var n in list)
    Console.WriteLine(n);

Dictionaries (Dictionary<TKey, TValue>)

  • What? Fast lookups by key (like a phone book).

  • When? You need to map keys to values.

var phoneBook = new Dictionary<string, string>();
phoneBook["Alice"] = "555-1234";

if (phoneBook.TryGetValue("Bob", out var number))
    Console.WriteLine(number);

Sets (HashSet<T> / SortedSet<T>)

  • What? Unique items: HashSet<T> for unordered, SortedSet<T> for sorted.

  • When? You need to eliminate duplicates.

var set = new HashSet<int> { 1, 2, 2, 3 };  // {1,2,3}

Stacks & Queues

  • Stack (LIFO): Last in, first out.

      var stack = new Stack<string>();
      stack.Push("first");
      stack.Push("second");
      Console.WriteLine(stack.Pop());  // "second"
    
  • Queue (FIFO): First in, first out.

      var queue = new Queue<string>();
      queue.Enqueue("hello");
      queue.Enqueue("world");
      Console.WriteLine(queue.Dequeue());  // "hello"
    

4. Tips (What I’ve learned)

  • Don’t modify in foreach: Changing a collection while iterating throws an error. Instead, use a for loop or iterate over list.ToList().

  • Reserve capacity: For large lists, use new List<T>(capacity) or set list.Capacity to reduce resizing costs.

  • Clear vs. recreate: list.Clear() keeps internal arrays; list = new List<T>() allocates new ones.

  • Bulk additions: Use AddRange to add many items at once.


5. Choosing the Right Collection

  • Fixed-size? Use arrays.

  • Dynamic list? Use List<T>.

  • Key/value pairs? Use Dictionary<TKey, TValue> (or SortedDictionary for ordered keys).

  • Unique items? Use HashSet<T> or SortedSet<T>.

  • Thread-safe? Use ConcurrentQueue<T> or ConcurrentDictionary<TKey, TValue>.


That’s a wrap

Now you know why collections matter, the main types in .NET, and how to avoid common mistakes. Try these collections in your next project, and watch your code become cleaner, faster, and more reliable.

If you have any questions or comments, feel free to reach out (louella.dev/socials) or leave a comment down below.

See ya!