Back to Curriculum

Collections and Generics

📚 Lesson 7 of 15 ⏱️ 45 min

Collections and Generics

45 min

C# provides generic collections like List<T> (dynamic arrays), Dictionary<K,V> (key-value pairs), HashSet<T> (unique values), and Queue<T> (FIFO), enabling efficient data storage and manipulation. Generic collections are type-safe and performant. Each collection type is optimized for specific use cases. Understanding collections enables effective data management. Collections are essential for most applications.

Generics allow type-safe collections and methods, enabling code reuse without sacrificing type safety. Generic types use type parameters (T, K, V) that are specified when using the type. Generics eliminate the need for casting and provide compile-time type checking. Understanding generics enables flexible, type-safe code. Generics are fundamental to modern C#.

LINQ provides powerful query capabilities for collections, enabling SQL-like queries on in-memory data. LINQ methods (Where, Select, OrderBy, etc.) enable filtering, transforming, and aggregating data. LINQ queries are readable and expressive. Understanding LINQ enables efficient data manipulation. LINQ is one of C#'s most powerful features.

Collections support iteration with foreach loops, enabling easy traversal of collection elements. foreach works with any type implementing IEnumerable<T>. foreach is simpler and safer than manual indexing. Understanding iteration enables processing collections. foreach is the preferred iteration method.

Different collection types serve different purposes: List<T> for ordered, indexed access; Dictionary<K,V> for key-value lookups; HashSet<T> for unique values; Queue<T> for FIFO operations; Stack<T> for LIFO operations. Choosing the right collection type improves performance and code clarity. Understanding collection types enables appropriate selection. Collection selection affects performance.

Best practices include using generic collections (avoid non-generic), choosing appropriate collection types, using LINQ for queries, understanding collection performance characteristics, and initializing collections with collection initializers. Understanding collections and generics enables efficient data management. Collections are fundamental to C# programming.

Key Concepts

  • C# provides generic collections: List<T>, Dictionary<K,V>, HashSet<T>, Queue<T>.
  • Generics enable type-safe collections and methods.
  • LINQ provides powerful query capabilities for collections.
  • Collections support iteration with foreach loops.
  • Different collection types serve different purposes.

Learning Objectives

Master

  • Using generic collections (List, Dictionary, HashSet, Queue)
  • Understanding generics and type parameters
  • Querying collections with LINQ
  • Choosing appropriate collection types

Develop

  • Understanding data structure selection
  • Designing efficient data access patterns
  • Appreciating generics' role in type safety

Tips

  • Use List<T> for dynamic arrays that need indexed access.
  • Use Dictionary<K,V> for key-value lookups.
  • Use HashSet<T> when you need unique values.
  • Use LINQ for querying collections—it's readable and powerful.

Common Pitfalls

  • Using non-generic collections, losing type safety.
  • Choosing wrong collection type, causing performance issues.
  • Not understanding LINQ deferred execution, causing unexpected behavior.
  • Modifying collections during iteration, causing exceptions.

Summary

  • C# provides powerful generic collections.
  • Generics enable type-safe, reusable code.
  • LINQ provides powerful query capabilities.
  • Understanding collections enables efficient data management.
  • Collections are essential for most applications.

Exercise

Demonstrate different collection types and LINQ operations.

using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
    static void Main(string[] args)
    {
        // List<T>
        List<string> names = new List<string> { "Alice", "Bob", "Charlie", "David" };
        names.Add("Eve");
        
        Console.WriteLine("Names:");
        foreach (var name in names)
        {
            Console.WriteLine(name);
        }
        
        // Dictionary<K,V>
        Dictionary<string, int> ages = new Dictionary<string, int>
        {
            ["Alice"] = 25,
            ["Bob"] = 30,
            ["Charlie"] = 35
        };
        
        Console.WriteLine("\nAges:");
        foreach (var kvp in ages)
        {
            Console.WriteLine($"{kvp.Key}: {kvp.Value}");
        }
        
        // LINQ operations
        var adults = names.Where(name => ages.ContainsKey(name) && ages[name] >= 18);
        Console.WriteLine("\nAdults:");
        foreach (var adult in adults)
        {
            Console.WriteLine(adult);
        }
        
        // LINQ with anonymous types
        var people = names.Select(name => new { Name = name, Age = ages.GetValueOrDefault(name, 0) });
        Console.WriteLine("\nPeople with ages:");
        foreach (var person in people)
        {
            Console.WriteLine($"{person.Name}: {person.Age}");
        }
    }
}

Code Editor

Output