5.4.7 Data Structures 2.0 6-Part Series (MSDN)

Last Updated: 9/28/2026
A Classic Data Structures Series in Modern Context

Scott Mitchell's An Extensive Examination of Data Structures Using C# 2.0 is a six-part series originally published on MSDN and revised in January 2005 for the then-new .NET Framework 2.0 and C# 2.0. It remains a useful guided tour of fundamental data structures, algorithmic complexity, and implementation tradeoffs.

The original companion downloads provide runnable samples and source code for the exercises in the series.

Install the runnable Data Structures 2.0 samples Download the Data Structures 2.0 source code
Why read a 2005 series?

The value of the series is not that every example represents how you should write C# today. Its value is that it makes the behavior of common data structures visible. Understanding why a Dictionary<TKey, TValue> can usually find an item quickly, why a binary search tree can become unbalanced, or why breadth-first search naturally uses a queue will make you better at choosing and using the modern collection types that .NET provides.

The 2005 revision marks an important transition

The January 2005 revision is historically interesting because it was updated for C# 2.0 and .NET Framework 2.0, when generics fundamentally changed collection programming in C#. A declaration such as List<Customer> or Dictionary<string, Customer> expresses the element types directly and avoids the casting required by older nongeneric collections.

Generic collections express intent directly
var names = new List<string>();
var customersById = new Dictionary<int, Customer>();
var pendingJobs = new Queue<Job>();
var undoHistory = new Stack<Edit>();
var selectedIds = new HashSet<int>();
Read the Six-Part Series

The links below point to Microsoft's archived Visual Studio 2005 Technical Articles versions. Read them in order if this is your first substantial study of data structures; later parts build on terminology and ideas introduced earlier.

Part 1: An Introduction to Data Structures

Part 1 introduces data structures, algorithm analysis, and the importance of comparing the cost of operations. It also examines arrays and lists.

Part 1 — An Introduction to Data Structures
Part 2: The Queue, Stack, and Hashtable

Part 2 examines FIFO and LIFO structures and introduces hash-based lookup through the Hashtable type.

Part 2 — The Queue, Stack, and Hashtable
Typical modern equivalents
var byUsername = new Dictionary<string, User>();
var work = new Queue<WorkItem>();
var history = new Stack<Command>();
Part 3: Binary Trees and BSTs

Part 3 introduces binary trees and binary search trees, including searching, insertion, deletion, and traversal. This material is especially useful for understanding the difference between a data structure's shape and its performance guarantees.

Part 3 — Binary Trees and BSTs
Part 4: Building a Better Binary Search Tree

Part 4 explores ways to avoid the pathological behavior of an ordinary binary search tree, including balanced trees and skip lists.

Part 4 — Building a Better Binary Search Tree
Part 5: From Trees to Graphs

Part 5 generalizes from trees to graphs and examines graph representation and traversal. Graph concepts remain directly relevant to routing, dependency analysis, social networks, build systems, scheduling, state transitions, and many other problems.

Part 5 — From Trees to Graphs
Part 6: Efficiently Representing Sets

Part 6 examines mathematical sets, general set operations, and disjoint-set structures.

Part 6 — Efficiently Representing Sets
Translate the Series to Modern .NET

A useful way to read the series is to separate three questions: What concept is being taught? What type did .NET provide in 2005? and What would I normally choose today?

Map historical examples to modern choices
From the series to modern .NET
Concept or need Historical or instructional emphasis Typical modern starting point
Resizable sequence ArrayList, arrays, early List<T> List<T>; arrays when fixed-size storage is appropriate
Key/value lookup Hashtable and hashing internals Dictionary<TKey, TValue>
FIFO processing Queue structure Queue<T>; ConcurrentQueue<T> when appropriate for concurrent access
LIFO processing Stack structure Stack<T>; ConcurrentStack<T> when appropriate for concurrent access
Unique values Custom set implementation HashSet<T>
Unique values in sorted order Tree-based set ideas SortedSet<T>
Sorted key/value data Balanced-tree concepts SortedDictionary<TKey, TValue> or SortedList<TKey, TValue> depending on workload
Priority-based removal Custom heap/priority-queue concepts in older material PriorityQueue<TElement, TPriority>
Graph traversal Custom nodes, edges, queues, stacks Domain model plus generic collections; choose representation for the problem
Disjoint sets Union/find implementation Often still a small purpose-built union-find/disjoint-set implementation or a suitable library
Program to useful collection contracts

When a method does not need the full concrete collection API, expose or accept the narrowest useful abstraction. Common examples include IEnumerable<T>, IReadOnlyCollection<T>, IReadOnlyList<T>, ICollection<T>, IList<T>, ISet<T>, and IDictionary<TKey, TValue>.

Accept only the capability the method requires
public decimal CalculateTotal(IEnumerable<OrderLine> lines)
{
    return lines.Sum(line => line.Price * line.Quantity);
}
Choose mutability and concurrency deliberately

The 2005 series predates much of today's collection ecosystem. Modern .NET includes concurrent and immutable collection families in addition to the familiar mutable generic collections.

Questions to ask before choosing a collection
  • Will multiple threads modify the collection concurrently?
  • Should callers be able to mutate the collection after it is exposed?
  • Will the data be built once and then read extremely frequently?
  • Is ordering required, or merely convenient for display?
  • Are duplicate values meaningful?
  • Is lookup by key more important than positional access?
  • Does allocation or copying matter on this code path?
Big-O survived every C# release

One of the most current parts of the series is its emphasis on algorithmic complexity. Language syntax changes. Libraries grow. The cost of repeatedly scanning an n-element sequence is still different from the expected cost of a hash lookup, and an accidentally quadratic algorithm still becomes painful as input grows.

A Good Way to Study the Series
First pass: read for concepts

On the first pass, focus on vocabulary and behavior: sequence, key, hash, collision, tree height, traversal, edge, set, union, and complexity. Do not get distracted by syntax that looks old.

Second pass: compare with today's API

When the article implements or discusses a collection, identify the closest modern .NET type and compare the public operations.

Suggested comparison routine
  1. Identify the abstract data structure being taught.
  2. Locate the closest current .NET collection type.
  3. Compare insertion, removal, lookup, enumeration, ordering, and duplicate behavior.
  4. Note whether the current type is mutable, read-only, immutable, concurrent, or frozen.
  5. Separate implementation details that are pedagogically useful from code you would actually maintain in an application.
Third pass: implement selected structures for learning

Reimplementing a structure can be valuable when the learning objective is the structure itself. A small BST, graph traversal, heap, or union-find implementation forces you to confront invariants that are easy to miss when calling a library method.

Current Microsoft .NET Collection Guidance

Use the archived series for the conceptual journey and current Microsoft documentation for API selection and current platform behavior.

Collections and Data Structures — .NET When to Use Generic Collections — .NET Commonly Used Collection Types — .NET Collections — C# Programming Guide
What to Carry Forward
Key ideas
  • Data-structure fundamentals and algorithmic complexity remain current even when APIs age.
  • Prefer generic collections such as List<T>, Dictionary<TKey, TValue>, Queue<T>, Stack<T>, and HashSet<T> for ordinary modern C# code.
  • Use sorted, concurrent, immutable, read-only, frozen, or priority-based collections when their semantics match the problem.
  • Implement classic structures yourself when the implementation is the learning objective, not merely because an old article did so.
  • Choose collections by required operations and semantics rather than by familiarity or name.
  • Use current Microsoft documentation to validate production API choices.