SortedDictionary in C# – Beginner to Advanced Guide
System.Collections.Generic.ICollection<System.Collections.Generic.KeyValuePair<TKey,TValue>>, System.Collections.Generic.IDictionary<TKey,TValue>, System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<TKey,TValue>>, System.Collections.Generic.IReadOnlyCollection<System.Collections.Generic.KeyValuePair<TKey,TValue>>, System.Collections.Generic.IReadOnlyDictionary<TKey,TValue>, System.Collections.IDictionary
If you're working with key/value pairs in C# and need them to be sorted by keys automatically, SortedDictionary<TKey, TValue> is your go-to collection.
In C#, the SortedDictionary<TKey, TValue> class is part of the System.Collections.Generic namespace. It is a powerful collection type used to store key-value pairs sorted by key. Unlike Dictionary<TKey, TValue>, which stores elements in arbitrary order, SortedDictionary ensures the keys are always maintained in a sorted order, using the default comparer or a custom one.
This article covers everything from basic usage to advanced scenarios, with practical code examples and performance considerations. Whether you're a beginner or an experienced .NET developer, you'll find this guide useful.
Key Characteristics
- It is used to store key/value pairs in the sorted order of key
- it does not store duplicate key. key must be unique.
- Key can not be null
- Value can be null in the case of reference type
- It allowed only the same type of key/value pair data
- Generic implementation of SortedDictionary<TKey,TValue> class is a binary search tree (BST). With retrieval complexity O(log n)
- SortedDictionary<TKey,TValue> requires higher memory than SortedList<TKey,TValue>.
- For unsorted data , SortedDictionary<TKey,TValue> performs faster insertion and removal operations in O(log n) compared to SortedList<TKey,TValue> which performs these operations in O(n)
- SortedList<TKey,TValue> is faster than SortedDictionary<TKey,TValue> if list is created all at once from sorted data.
Why Use SortedDictionary in C#?
- You want sorted access to your data without manual sorting.
- You need logarithmic time complexity for insertions/removals/lookups.
- You’re storing dynamically changing data and still want it sorted.
- You want predictable iteration order based on key sorting.
SortedDictionary<TKey,TValue> Constructors
The SortedDictionary<TKey, TValue> class has four main constructors
1. Default constructor (uses default sorting)
Creates an empty dictionary using the default comparer, which uses IComparable<TKey> or Comparer<TKey>.Default
public SortedDictionary ();
var dict1 = new SortedDictionary<int, string>(); dict1.Add(3, "C#"); dict1.Add(1, "Java"); dict1.Add(2, "Python"); foreach (var kv in dict1) Console.WriteLine($"Key: {kv.Key}, Value: {kv.Value}"); // Output sorted by key: 1 → Java, 2 → Python, 3 → C#
This example uses the default comparer, so the numeric keys sort in ascending order.
2. Custom comparer
Creates an empty dictionary using a custom comparer to define key order
public SortedDictionary (System.Collections.Generic.IComparer<TKey> comparer);
class DescendingComparer<T> : IComparer<T> where T : IComparable<T> { public int Compare(T x, T y) => y.CompareTo(x); } var dict2 = new SortedDictionary<int, string>(new DescendingComparer<int>()); dict2.Add(3, "Three"); dict2.Add(1, "One"); dict2.Add(2, "Two"); foreach (var kv in dict2) Console.WriteLine($"{kv.Key}: {kv.Value}"); // Output: 3 → Three, 2 → Two, 1 → One
Here the custom comparer reverses numeric order.
3. Construct from existing dictionary
Copies elements from an existing dictionary and uses the default comparer.
public SortedDictionary (System.Collections.Generic.IDictionary<TKey,TValue> dictionary);
var init = new Dictionary<string, int> { ["apple"] = 5, ["banana"] = 3, ["cherry"] = 7 }; var dict3 = new SortedDictionary<string, int>(init); foreach (var kv in dict3) Console.WriteLine($"{kv.Key}: {kv.Value}"); // Output: apple → 5, banana → 3, cherry → 7 (alphabetic order)
This copies from an ordinary dictionary and orders by key naturally.
4. Construct from dictionary with custom comparer
Copies elements from an existing dictionary and uses a custom comparer.
public SortedDictionary (System.Collections.Generic.IDictionary<TKey,TValue> dictionary, System.Collections.Generic.IComparer<TKey>? comparer);
var original = new Dictionary<int, string> { [10] = "Ten", [5] = "Five", [20] = "Twenty", [15] = "Fifteen" }; var comparer = Comparer<int>.Create((a, b) => b.CompareTo(a)); var sorted = new SortedDictionary<int, string>(original, comparer); foreach (var kv in sorted) Console.WriteLine($"Key = {kv.Key}, Value = {kv.Value}");
Key = 20, Value = Twenty Key = 15, Value = Fifteen Key = 10, Value = Ten Key = 5, Value = Five
SortedDictionary<TKey,TValue> Properties
- Comparer :- public System.Collections.Generic.IComparer<TKey> Comparer { get; }Gets the IComparer<T> that is used to sort/order element of the SortedDictionary<TKey,TValue>.
- Count :- public int Count { get; }Gets the number of key/value pairs that is present in the SortedDictionary<TKey,TValue>. it is an O(1) operation.
- Item[TKey] :- public TValue this[TKey key] { get; set; }Gets or sets the value linked with the specified key. Getting and setting the value of this property, both is an O(log n) operation.
- Keys :- public System.Collections.Generic.SortedDictionary<TKey,TValue>.KeyCollection Keys { get; }Gets a KeyCollection containing the keys in the SortedDictionary<TKey,TValue>. it is also an O(1) operation.
- Values :- public System.Collections.Generic.SortedDictionary<TKey,TValue>.ValueCollection Values { get; }Gets a ValueCollection containing the values in the SortedDictionary<TKey,TValue>. it is an O(1) operation.
using System; using System.Collections.Generic; class Example { static void Main() { // Create a SortedDictionary<string, string> var openWith = new SortedDictionary<string, string>(); openWith.Add("txt", "notepad.exe"); openWith.Add("bmp", "paint.exe"); openWith.Add("dib", "paint.exe"); openWith.Add("rtf", "wordpad.exe"); // 1) Count Console.WriteLine($"Count = {openWith.Count}"); // 2) Comparer Console.WriteLine($"Comparer = {openWith.Comparer}"); // 3) Item[TKey] indexer: get and set Console.WriteLine($"Value for 'rtf' = {openWith["rtf"]}"); openWith["rtf"] = "winword.exe"; Console.WriteLine($"Updated value for 'rtf' = {openWith["rtf"]}"); // 4) Keys collection Console.WriteLine("Keys:"); foreach (var k in openWith.Keys) Console.WriteLine($"- {k}"); // 5) Values collection Console.WriteLine("Values:"); foreach (var v in openWith.Values) Console.WriteLine($"- {v}"); } }
Count = 4 Comparer = System.Collections.Generic.GenericComparer`1[System.String] Value for 'rtf' = wordpad.exe Updated value for 'rtf' = winword.exe Keys: - bmp - dib - rtf - txt Values: - paint.exe - paint.exe - winword.exe - notepad.exe
Creating and adding element in SortedDictionary<TKey,TValue>
using System; using System.Collections.Generic; class Program { static void Main() { var dict = new SortedDictionary<string,string> { {"language","C#"}, {"topic","SortedDictionary"}, {"label","Beginner"} }; Console.WriteLine("We are learning "+dict["language"]+" "+dict["topic"] +" and level : "+dict["label"]); Console.WriteLine("------------------loop------------"); foreach (KeyValuePair<string, string> kv in dict) { Console.WriteLine("K: "+ kv.Key+" V: "+kv.Value); } } }
We are learning C# SortedDictionary and level : Beginner ------------------loop------------ K: label V: Beginner K: language V: C# K: topic V: SortedDictionary
using System; using System.Collections.Generic; class Program { static void Main() { var dict = new SortedDictionary<string,string>(); dict["language"]="C#"; dict["topic"]="SortedDictionary"; dict["label"]="Beginner"; Console.WriteLine("We are learning "+dict["language"]+" "+dict["topic"] +" and level : "+dict["label"]); Console.WriteLine("------------------loop------------"); foreach (KeyValuePair<string, string> kv in dict) { Console.WriteLine("K: "+ kv.Key+" V: "+kv.Value); } } }
We are learning C# SortedDictionary and level : Beginner ------------------loop------------ K: label V: Beginner K: language V: C# K: topic V: SortedDictionary
using System; using System.Collections.Generic; class Program { static void Main() { var dict = new SortedDictionary<string,string>(); dict.Add("language","C#"); dict.Add("topic","SortedDictionary"); dict.Add("label","Beginner"); Console.WriteLine("We are learning "+dict["language"]+" "+dict["topic"] +" and level : "+dict["label"]); Console.WriteLine("------------------loop------------"); foreach (KeyValuePair<string, string> kv in dict) { Console.WriteLine("K: "+ kv.Key+" V: "+kv.Value); } } }
We are learning C# SortedDictionary and level : Beginner ------------------loop------------ K: label V: Beginner K: language V: C# K: topic V: SortedDictionary
Looping Through SortedDictionary
foreach (var kvp in dict) { Console.WriteLine($"Key: {kvp.Key}, Value: {kvp.Value}"); } foreach (var key in dict.Keys) { Console.WriteLine("Key: " + key); } foreach (var value in dict.Values) { Console.WriteLine("Value: " + value); }
Exceptions You Might Encounter
1. ArgumentNullException
Keys in SortedDictionary cannot be null. This exception is thrown early because null keys break the comparer logic.
So, Passing null as the key, either in Add(key, value), dict[key] = value, dict.Remove(null) or retrieving via indexer will throw ArgumentNullException.
var dict = new SortedDictionary<string, int>(); // Throws ArgumentNullException because key is null dict.Add(null!, 5);
2. ArgumentException
SortedDictionary enforces unique keys. If a key already exists (per comparer logic), Add() throws ArgumentException.
var dict = new SortedDictionary<string, int>(); dict.Add("apple", 1); dict.Add("banana", 2); try { dict.Add("apple", 10); } catch (ArgumentException ex) { Console.WriteLine("ArgumentException: " + ex.Message); }
ArgumentException: An item with the same key has already been added. Key: [apple, 10]
3. KeyNotFoundException
If the key is not present, accessing a non-existent key via the indexer: var v = dict[key]; throws KeyNotFoundException exception
var dict = new SortedDictionary<string, int> { ["apple"] = 1 }; try { var val = dict["banana"]; } catch (KeyNotFoundException ex) { Console.WriteLine("KeyNotFoundException: " + ex.Message); }
KeyNotFoundException: The given key 'banana' was not present in the dictionary.
Comparisons: SortedDictionary vs Dictionary vs SortedList
Feature | Dictionary | SortedDictionary | SortedList |
---|---|---|---|
Order | Unordered | Sorted by key | Sorted by key |
Lookup Complexity | O(1) | O(log n) | O(log n) |
Insert/Delete | O(1) avg | O(log n) | O(n) |
Memory Use | Low | High | Medium |
Best For | Fast access | Dynamic sorted data | Static sorted data |