C# Data Structures
Data structures are a way of organizing and storing data in a computer so that it can be used efficiently.
Some of the main types of data structures are:
Arrays - Arrays store data in a linear fashion, using indexes to access the data. They are fast for sequential access but slow for random access.
Lists - Lists store data in a linear fashion like arrays, but allow dynamic resizing and insertion/deletion at any position.
Stacks - Stacks follow the LIFO (Last In First Out) principle. They are useful for implementing function calls, undo operations etc.
Queues - Queues follow the FIFO (First In First Out) principle. They are useful for implementing job scheduling, print queues etc.
Linked Lists - Linked Lists store data in nodes connected by links/pointers. They allow efficient insertion/deletion but slower access by index.
Trees - Trees store data in a hierarchical fashion. They are useful for storing hierarchical data and for searching.
Graphs - Graphs store data in nodes connected by edges. They are useful for modeling networks and relations.
Hash Tables - Hash Tables store data in an array-like structure, using a hash function to compute the index based on a key. They allow very efficient lookup, insertion and deletion of data.
The choice of data structure depends on the operations that need to be performed efficiently. For example, if you need fast random access, an array would be better than a linked list. But if you need frequent insertions/deletions, a linked list would be better.
DS Libraries
In C#, the strategy to define data structures is to use the built-in types and classes provided by the .NET framework libraries. Some of the main data structures predefined in the .NET libraries are:
• Collections - Like List, Dictionary<TKey,TValue>, Stack, Queue etc. These represent common collection data structures like arrays, lists, stacks, queues etc.
• Strings - Represents text data. The String class provides many useful methods for manipulating strings.
• Dates and Times - The DateTime structure allows us to work with dates and times.
• Files and Streams - Classes like File, FileStream, MemoryStream etc. allow us to work with files and streams of data.
• Regular Expressions - The Regex class allows us to define and work with regular expressions.
• Networking - Classes like Socket, WebRequest etc. allows us to work with network resources.
As a C# developer, we typically don't need to implement our own data structures from scratch. We simply need to learn:
What data structures are available in the .NET libraries
When to use each data structure based on our requirements
How to use each data structure by learning its API
This allows us to focus on the business logic of our applications, rather than reinventing basic data structures. The .NET framework provides a robust set of predefined data structures that cover most common use cases.
So in summary, the strategy in C# is to use the data structures provided in the .NET libraries, rather than implementing our own from scratch. We just need to learn how and when to utilize each structure based on our needs.
Arrays
Arrays allow us to store multiple values of the same type in an ordered collection.
We declare arrays in C# using the following syntax:
dataType[] arrayName = new dataType[size];
For example:
int[] numbers = new int[5];
string[] names = new string["John", "Jane", "Andy"];
We can initialize arrays with data literals:
int[] numbers = {1, 2, 3};
string[] names = {"John", "Jane", "Andy"};
We access array elements using indexes starting from 0:
numbers[0] // Returns 1
names[1] // Returns "Jane"
We can have multi-dimensional arrays:
int[,] matrix = new int[3,4];
And jagged arrays (arrays of arrays):
int[][] jaggedArray = new int[3][];
jaggedArray[0] = new int[2] {1, 2};
jaggedArray[1] = new int[4] {2, 4, 6, 8};
Generics
Before diving into the various collection classes in C#, it's better to first learn about generics. Here's why:
Most collection classes in C# are implemented as generics. This means they can contain elements of any type.
Without understanding generics, you won't be able to properly declare and use collection classes. For example:
List - A list of integers
List - A list of strings
The and specify the type of elements the list can contain.
Generics allow you to write reusable collection classes that can work with different types. This saves a lot of code duplication.
Many methods and properties in collection classes use generics in their signatures. For example:
List.Add(T item)
Dictionary<TKey, TValue>.Add(TKey key, TValue value)
So in short, generics are fundamental to how collection classes are implemented and used in C#. Without understanding generics, you'll struggle to properly utilize collections.
Some key things to learn about generics:
Generic type parameters (T, TKey, TValue etc.)
Generic class and method declarations
Constraints on generic type parameters
Once you have a good grasp of generics, learning the various collection classes will be much easier since you'll understand their generic implementations.
So in summary, I do recommend learning generics first, before diving into specific collection classes in C#. The knowledge of generics will provide a solid foundation for utilizing collections effectively.
Complexity
Generics are more difficult to implement but easy to use for the following reasons:
• Implementation complexity: Generics require complex implementation techniques like type erasure and reification to work properly. The compiler and runtime have to handle generic types in a special way. This makes implementing generic classes and methods a complex task.
• Algorithms already implemented: But once generic collections and algorithms are implemented, the user does not need to worry about the implementation details. The user just specifies the type parameter and uses the ready made generic classes and methods.
For example:
List numbers = new List(); // User specifies int type numbers.Add(5); // User calls ready made Add() method
Dictionary<string, Employee> employees = new Dictionary<string,Employee>(); employees.Add("John", new Employee()); // User specifies key and value types // and calls ready made Add() method
The user does not need to implement their own List or Dictionary - they just specify the type parameters and use the ready made generic implementations.
• Complex type erasure at runtime: At runtime, the generic type information is erased and a single implementation is used for all types. This type erasure is complex to implement but transparent to the user. The user just specifies the type at compile time.
So in summary, while implementing generics requires complex techniques like type erasure, reification, constraints etc., once implemented - generics are easy for the user to utilize. The user simply specifies the type parameter and uses the ready made generic classes and algorithms, without needing to worry about the implementation details.
So yes, generics fit the pattern of being:
Difficult to implement
But easy to use (once implemented)
Because the user gets to leverage ready made generic collections and algorithms, without needing to understand how they were implemented.
References:
Here are the hyperlinks to the resources I mentioned:
Microsoft Docs - Collection Types
https://docs.microsoft.com/en-us/dotnet/api/system.collections?view=net-6.0TutorialsPoint - C# Collections https://www.tutorialspoint.com/csharp/csharp_collections.htm
Disclaim: The links provided may or may not work. This list was created with AI Rix.
Subscribe to my newsletter
Read articles from Elucian Moise directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Elucian Moise
Elucian Moise
Software engineer instructor, software developer and community leader. Computer enthusiast and experienced programmer. Born in Romania, living in US.