LogIn
I don't have account.

SortedDictionary in C# – Beginner to Advanced Guide

DevSniper

143 Views
Namespace :- System.Collections.Generic
Assembly :- System.Collections.dll
Signature :-
public class SortedDictionary<TKey,TValue> :
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 ();
Copy
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);
Example descending order
Copy
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);
Copy
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);
Copy
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.
Example
Copy
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>

1. initialize the SortedDictionary<TKey,TValue> with initial values
Copy
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
2. Direct Assign to add new element in SortedDictionary<TKey,TValue>
Copy
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
3. Using Add() Method to add new element in SortedDictionary<TKey,TValue>
Copy
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

Copy
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.

Copy
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.

Copy
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

Copy
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