Collections
Type-safe lists, dictionaries, sets, and queues
Introduction
In this lesson, you'll learn about collections in C#. Coming from JavaScript, you already have a foundation for understanding this concept. We'll build on that knowledge while highlighting the key differences.
In JavaScript, you're familiar with type-safe lists, dictionaries, sets, and queues.
C# has its own approach to type-safe lists, dictionaries, sets, and queues, which we'll explore step by step.
The C# Way
Let's see how C# handles this concept. Here's a typical example:
using System.Collections.Generic;
// List<T>
var nums = new List<int> { 1, 2, 3 };
nums.Add(4);
nums.Contains(2); // true
nums.Where(x => x > 1); // LINQ (returns IEnumerable)
nums.Select(x => x * 2); // LINQ map
// Dictionary<K,V>
var dict = new Dictionary<string, int>();
dict["key"] = 42;
dict.TryGetValue("key", out int val); // safe get
dict.ContainsKey("key"); // true
dict.Remove("key");
// HashSet<T>
var set = new HashSet<int> { 1, 2, 3 };
set.Add(4);
set.Contains(3); // true
// Queue / Stack
var q = new Queue<string>();
q.Enqueue("first");
q.Dequeue(); // "first"
var stack = new Stack<int>();
stack.Push(1);
stack.Pop(); // 1Comparing to JavaScript
Here's how you might have written similar code in JavaScript:
// Array
const nums = [1, 2, 3];
nums.push(4);
nums.includes(2); // true
nums.filter(x => x > 1);
nums.map(x => x * 2);
// Map
const map = new Map();
map.set("key", 42);
map.get("key"); // 42
map.has("key"); // true
map.delete("key");
// Set
const set = new Set([1, 2, 3]);
set.add(4);
set.has(3); // trueYou may be used to different syntax or behavior.
List<T> replaces JS Array with type safety; Add() vs push()
You may be used to different syntax or behavior.
Dictionary<K,V> replaces Map; use TryGetValue for safe access
You may be used to different syntax or behavior.
HashSet<T> replaces Set; same Add/Contains/Remove semantics
You may be used to different syntax or behavior.
LINQ methods (Where/Select) work on any IEnumerable<T> — not just List
You may be used to different syntax or behavior.
All generic collections live in System.Collections.Generic namespace
Step-by-Step Breakdown
1. List<T>
List<T> is the go-to sequential collection. It auto-resizes like JS arrays but is type-safe.
const list = []; list.push(1); list.includes(1);var list = new List<int>();
list.Add(1);
list.Contains(1);2. Dictionary<K,V>
Dictionaries store key-value pairs. Use TryGetValue to avoid KeyNotFoundException on missing keys.
const m = new Map(); m.set("k",1); m.get("k");var d = new Dictionary<string,int>();
d["k"] = 1;
if (d.TryGetValue("k", out int v)) Console.WriteLine(v);3. LINQ on Collections
LINQ extends all IEnumerable<T> collections with functional methods. Import System.Linq.
nums.filter(x => x > 1).map(x => x * 2)nums.Where(x => x > 1).Select(x => x * 2).ToList()4. Collection Initialization
C# supports collection initializers — a concise syntax for creating pre-populated collections.
const nums = [1, 2, 3];
const map = { a: 1, b: 2 };var nums = new List<int> { 1, 2, 3 };
var map = new Dictionary<string,int> { ["a"]=1, ["b"]=2 };Common Mistakes
When coming from JavaScript, developers often make these mistakes:
- List<T> replaces JS Array with type safety; Add() vs push()
- Dictionary<K,V> replaces Map; use TryGetValue for safe access
- HashSet<T> replaces Set; same Add/Contains/Remove semantics
Key Takeaways
- List<T> for ordered items, Dictionary<K,V> for key-value, HashSet<T> for unique items
- TryGetValue is the safe way to access dictionary values
- LINQ (Where/Select/GroupBy) works on any IEnumerable<T>
- Collection initializers make inline population clean and readable