C# Programming Tutorial 0/45 lessons ~6 min read Lesson 22

    Lists

    List<T> is the default mutable collection — dynamic array backing with amortized O(1) Add.

    Course progress0%
    Focus
    10 guided sections
    Practice signal
    Examples included
    Career prep
    Interview Q&A included

    Introduction

    List<T> is the default mutable collection — dynamic array backing with amortized O(1) Add. It implements IList, IEnumerable, and is what most business code uses before persisting to databases.

    Capacity doubles on growth — understanding this helps memory profiling. ReadOnlyCollection wraps List for exposing immutable views. Interviewers ask List vs LinkedList (rare in .NET LOB) and thread safety (List is not thread-safe).

    The story

    An online electronics store builds a shopping cart as a list of line items — each with a SKU, quantity, and unit price. Customers add items throughout their session; the cart computes a running total and can show the three most expensive products before checkout.

    Lists grow dynamically unlike arrays, which is why carts, wish lists, and in-memory order drafts use List<T> everywhere in e-commerce backends.

    Understanding the topic

    Key concepts

    • List resizable array implementation.
    • Add, Insert, Remove, RemoveAt, Clear operations.
    • Capacity vs Count — pre-size with capacity constructor.
    • Find, FindAll, Sort, BinarySearch on sorted lists.
    • Not thread-safe — use ConcurrentBag or lock.
    • Collection expressions can create List with [items].

    Step-by-step explanation

    1. Add appends at Count; grows capacity when full.
    2. Insert shifts elements — O(n).
    3. Remove scans for equality — O(n).
    4. Sort uses introspective sort O(n log n).
    5. foreach uses struct enumerator — minimal allocation.
    6. ToList materializes LINQ query into List.

    Practical code example

    Order line items with List operations and capacity hint:

    csharp
    namespace TechLearningPro.Lists;
    public sealed record LineItem(string Sku, int Quantity, decimal UnitPrice);
    public sealed class Order
    {
    private readonly List<LineItem> _lines = new(capacity: 8);
    public IReadOnlyList<LineItem> Lines => _lines;
    public void AddLine(LineItem item)
    {
    ArgumentNullException.ThrowIfNull(item);
    _lines.Add(item);
    }
    public decimal Total => _lines.Sum(l => l.Quantity * l.UnitPrice);
    public List<LineItem> TopExpensiveLines(int n) =>
    _lines.OrderByDescending(l => l.UnitPrice).Take(n).ToList();
    }

    Line-by-line code explanation

    • sealed record LineItem(string Sku, int Quantity, decimal UnitPrice) models one cart row.
    • private readonly List<LineItem> _lines = new(capacity: 8) pre-allocates space for typical cart sizes.
    • public IReadOnlyList<LineItem> Lines => _lines exposes a read-only view — callers cannot bypass AddLine.
    • AddLine(LineItem item) is the controlled way to mutate the cart.
    • ArgumentNullException.ThrowIfNull(item) rejects null items at the API boundary.
    • _lines.Add(item) appends the item to the internal list.
    • Total => _lines.Sum(l => l.Quantity * l.UnitPrice) computes cart total with LINQ.
    • OrderByDescending(l => l.UnitPrice) sorts lines by price descending.
    • Take(n).ToList() materializes the top N expensive lines into a new list.

    Key takeaway: Expose IReadOnlyList externally; mutate via methods. capacity: 8 avoids early reallocations for typical cart sizes.

    Real-world use

    Where you'll use this in production

    • In-memory shopping carts before checkout.
    • Aggregating API page results client-side.
    • Building CSV export rows.
    • Caching hot lookup lists in memory.

    Best practices

    • Expose IReadOnlyList, keep List private.
    • Preallocate capacity when size estimate known.
    • Use RemoveAll with predicate vs manual loop.
    • Sort once before repeated BinarySearch.
    • Consider ImmutableList for shared concurrent reads.

    Common mistakes

    • Modifying list during foreach — InvalidOperationException.
    • Returning internal List allowing external mutation.
    • LinkedList for general use — poor cache locality.
    • Contains on large lists — use HashSet for lookups.

    Advanced interview questions

    Q1BeginnerList growth strategy?
    Double capacity when full — amortized O(1) Add.
    Q2BeginnerList thread-safe?
    No — synchronize or use concurrent collections.
    Q3IntermediateList vs IList interface?
    List concrete class; IList abstraction for APIs.
    Q4IntermediateRemove item while iterating?
    for reverse index loop or RemoveAll predicate.
    Q5AdvancedDesign paginated in-memory cache of 1M items.
    List too large — use dictionary by key; LRU with LinkedList+Dictionary; avoid List.Contains O(n).

    Summary

    List is the standard mutable dynamic array collection. Protect internal list; expose read-only views. Pre-size capacity and avoid modify-during-enumerate bugs. LINQ integrates seamlessly with List. Next: Dictionary for key-value lookups.

    Ready to mark this lesson complete?Track your journey across the entire course.