C# Advance

C# SortedList

The objective of this article is to familiarize you with the generic C# SortedList, which is implemented via SortedList<TKey, TValue> collection class. You will learn how to declare, create, instantiate and use this type of collection, including code examples of the commonly used methods and properties of SortedList<> class. Finally, different ways to iterate through all the items of the collection.

C# SortedList<TKey, TValue> (generics)

C# SortedList<TKey, TValue> represents a generic sorted collection class, which is used to store data in a key/value pair format that is sorted by key. Key must be unique and cannot be null whereas value can be duplicate and null. Each item in the SortedList is treated as KeyValuePair<TKey, TValue> structure that associates a key to a value. You can get the associated value using the unique key as an index with the indexer method. Each item can be retrieved as a KeyValuePair<TKey, TValue> object.

C# SortedList can be implemented with the SortedList<TKey, TValue> class in the System.Collections.Generic namespace, where TKey and TValue are type parameters.

Common Properties and Methods of the SortedList<> Class

Mostly used methods of the SortedList<> class to perform different operations are listed below.

SortedList-TKey,TValue

You can declare and initialize C# SortedList as in the following code.

using Statement

To work with C# SortedList, you need to add the following namespace at the beginning your code:

using System.Collections.Generic;

Declaration and Instantiation Using Two Statements

TKey and TValue are the generic data type parameters, where TKey is the type of the Key and TValue is the type of the value. This part of the declaration indicates that the class will work with a certain type.

SortedList<TKey, TValue> sortedListName; // declaration

You use new keyword with default constructor to initialize an empty sorted list.

sortedListName = new SortedList<TKey, TValue>(); // initialization

You can set the initial capacity of a sorted list by specifying the number of items in parentheses () when a list is created. If the number of items in the list exceeds the capacity, the capacity is automatically doubled to accommodate more items.

sortedListName = new SortedList<TKey, TValue>(capacity);

The capacity can be decreased by calling TrimExcess method or by setting the Capacity property explicitly.

Declaration and Instantiation Combined on a Single Line

SortedList<TKey, TValue> sortedListName = new SortedList<TKey, TValue>(); // declaration & initialization

Use of var keyword to infer its type from the value that’s assigned to it.

var sortedListName = new SortedList<TKey, TValue>();

Collection Initializers

You can assign values to collections using collection initializer syntax introduced in C# 3.0. It is a shortened syntax to create an instance of a collection. With a collection initializer, the values can be assigned all at once during the declaration using curly brackets notation. When the C# compiler encounters a collection initializer, it’ll replace the shorthand initializer syntax by appropriate method calls to the Add method.

Notice that because each item of a sorted list consists of two values, each item is enclosed in braces within the outer braces. Key names must be unique when populating the collection.

Here, you can specify each key/value pair as an anonymous type in the initializer list, like this:

SortedList<int, string> planets = new SortedList<int, string>() {
    { 1, "Mercury" },
    { 2, "Venus" },
    { 3, "Earth" }
};

If no parameters are passed in the constructor, then parentheses () following the data type are optional.

SortedList<int, string> planets = new SortedList<int, string> {
    { 1, "Mercury" },
    { 2, "Venus" },
    { 3, "Earth" }
};

The following collection initializer uses object initializers to initialize objects of the Planet class. Individual object initializers are enclosed in braces and separated by commas. Here, you are using int data type as the key (key can be any type) and a Planet type as the value.

var planets = new SortedList<int, Planet> {
    { 101, new Planet() { Id = 1, Name = "Mercury" } },
    { 102, new Planet() { Id = 2, Name = "Venus" } },
    { 103, new Planet() { Id = 3, Name = "Earth" } }
};

Run Demo

Index Initializers (indexer notation)

C# 6 introduced a new feature called index initializer if a collection supports indexing, where you don’t need key and value within braces { }. Instead you use the key within brackets [ ] followed by assignment operator (=) to assign value to that key.

Note: The new initializer syntax calls the indexer method to add and replace items to the collection.

//With an index initializer inside a collection initializer
SortedList<int, string> planets = new SortedList<int, string>()
{
    [1] = "Mercury",
    [2] = "Venus",
    [3] = "Earth"
};

To make your code more readable, I recommend that you use the indexer notation wherever possible to initialize a SortedList type.

Run Demo

Add Items to a SortedList

SortedList<int, string> planets = new SortedList<int, string>();

Add()

Use the Add method to add an item to the list. When you add an item to a sorted list, you specify the key along with the value assigned to that key. The SortedList automatically ensures that the items in the set are sorted when you insert or remove items.

Signature:void Add(TKey key, TValue value)

//With separated statements
planets.Add(2, "Venus");
planets.Add(1, "Mercury");
planets.Add(3, "Earth");

Run Demo

OUTPUT

1, Mercury
2, Venus
3, Earth

Every key in a SortedList<TKey, TValue> must be unique, meaning you cannot use duplicate keys in the list. Any attempt to do that will result an exception ArgumentException with the message 'An item with the same key has already been added. Key: 3'

// Different key but same value
planets.Add(4, "Earth");

//Same key but different value
planets.Add(3, "Mars"); // Exception

Run Demo

Using add method to add individual objects of Planet class.

SortedList<int, Planet> planets = new SortedList<int, Planet>();
var jupiter = new Planet();
jupiter.Id = 1;
jupiter.Name = "4 line: Mercury";
planets.Add(101, jupiter);

//Above 4 lines vs below 1 line syntax. Same result
planets.Add(102, new Planet() { Id = 2, Name = "1 line: Mercury" });

Run Demo

OUTPUT

101, 1 : 4 line: Mercury
102, 2 : 1 line: Mercury

Key Indexer to Add and Update Items

If a key does not exist, setting the indexer for that key adds a new key/value pair in the sorted list.

planets = new SortedList<int, string>();

// using array notation
planets[1] = "Mercury";
planets[2] = "Venus";
planets[3] = "Earth";

Use key indexer to update value associated with the key.

planets[3] = "Earth - Updated";

Run Demo

OUTPUT

1, Mercury
2, Venus
3, Earth- Updated

Remove SortedList Items

SortedList<int, string> planets = new SortedList<int, string> {
    { 1, "Mercury" },
    { 2, "Venus" },
    { 3, "Earth" }
};

Remove()

The Remove method removes a particular item from the list. This method takes in one argument and removes the first occurrence of that argument. You need to pass the key to remove.

Signature:bool Remove(TKey key)

planets.Remove(2);

Run Demo

OUTPUT

1, Mercury
3, Earth

RemoveAt()

The RemoveAt method removes an item from a specific location in the list by passing an index.

Signature:void RemoveAt(int index)

planets.RemoveAt(1);

Run Demo

OUTPUT

1, Mercury
3, Earth

Clear()

The Clear method removes all the items in a list and sets its Count property to zero.

Signature:void Clear()

planets.Clear();

Run Demo

ContainsKey(), ContainsValue(), IndexOfKey(), IndexOfValue(), TryGetValue(), and TrimExcess() Methods

SortedList<int, string> planets = new SortedList<int, string> {
    { 1, "Mercury" },
    { 2, "Venus" },
    { 3, "Earth" }
};

ContainsKey()

You can test whether a SortedList<TKey, TValue> collection already contains a particular key by using the ContainsKey method, which returns Boolean (True/False) value.

Signature:bool ContainsKey(TKey key)

planets.ContainsKey(2); // Returns True

Run Demo

OUTPUT

True

ContainsValue()

Method checks and returns Boolean (True/False) value that indicates whether or not the sorted list contains the specified value.

Signature:bool ContainsValue(TValue value)

planets.ContainsValue("Venus"); // Returns True

Run Demo

OUTPUT

True

IndexOfKey()

Method checks entire SortedList<TKey, TValue> for the provided key and returns zero-based index value.

Signature:int IndexOfKey(TKey key)

planets.IndexOfKey(3); //Returns 2

Run Demo

IndexOfValue()

Method checks SortedList<TKey, TValue> for the first occurrence of the provided value and returns zero-based index value.

Signature:int IndexOfValue(TValue value)

planets.IndexOfValue("Venus"); //Returns 1

Run Demo

TryGetValue()

Gets the value associated with the specified key.

Signature:bool TryGetValue(TKey key, out TValue value)

string value = string.Empty;
planets.TryGetValue(3, out value);
OUTPUT

Earth

// Or

if (planets.TryGetValue(4, out value))
    Console.WriteLine(value);
else
    Console.WriteLine("Key not found");
OUTPUT

Key not found

Run Demo

TrimExcess()

Resizes the capacity to the actual number of items in the SortedList<TKey, TValue>, if that number is less than 90 percent of current capacity.

Signature:void TrimExcess()

var planets = new SortedList<int, string>(6);

Console.WriteLine($"Capacity : {planets.Capacity}"); // Capacity before TrimExcess() is 6

planets.Add(1, "Mercury");
planets.Add(2, "Venus");
planets.Add(3, "Earth");

planets.TrimExcess();

Console.WriteLine($"Capacity : {planets.Capacity}"); // Capacity after TrimExcess() is 3

Run Demo

Count, Capacity, Keys, and Values Properties

SortedList<int, string> planets = new SortedList<int, string> {
    { 1, "Mercury" },
    { 2, "Venus" },
    { 3, "Earth" }
};

Count

To get the total number of key/value pairs contained in the SortedList<TKey, TValue>, use the Count property.

planets.Count; // Returns 3

Run Demo

Capacity

The Capacity property gets or sets the number of items a SortedList<TKey, TValue> can hold. It is automatically increased as required when items are added to it.

The default capacity of a list is 0; if initialized as an empty list. As you can see in the following example.

planets = new SortedList<int, string>();
planets.Capacity; //Returns 0

The capacity automatically assigned to a list is 4, even if initialized with a single item. As soon as the number of items in a list exceeds 4, the list is resized to accommodate more items. With every resizes the capacity is automatically doubled from 4 to 8, 16, 24 etc. Check the below example.

planets = new SortedList<int, string>() { { 1, "Mercury" } };
planets.Capacity; //Returns 4

Set the initial capacity of a sorted list by specifying the number in parentheses () when a list is created.

planets = new SortedList<int, string>(7); //Constructor

You can set the capacity later in the code, once sorted listed is created.

planets = new SortedList<int, string>();
planets.Capacity; // Default initial capacity is 0
planets.Capacity = 5; //Set capacity to 5

If you set the capacity after initialization of a sorted list and the number of items exceeds that capacity, an ArgumentOutOfRangeException exception is thrown with the message 'capacity was less than the current size.’

planets = new SortedList<int, string>() {
    { 1, "Mercury" },
    { 2, "Venus" },
    { 3, "Earth" },
    { 4, "Mars" }
};

planets.Capacity = 3; // Set capacity to 3

Run Demo

Keys

To get a collection containing the keys in the SortedList<TKey, TValue>, in sorted order, use the Keys property.

Note: Keys property returns IList

foreach (var key in planets.Keys)
    Console.WriteLine(key);

// Or copy all the keys and then iterate
IList keys = planets.Keys;

foreach (var key in keys)
    Console.WriteLine(key);

Run Demo

OUTPUT

1
2
3

Values

To get a collection containing the values in the SortedList<TKey, TValue>, use the Values property.

Note: Values property returns IList

foreach (var value in planets.Values)
    Console.WriteLine(value);

// Or copy all the values and then iterate
IList values = planets.Values;

foreach (var value in values)
    Console.WriteLine(value);

Run Demo

OUTPUT

Mercury
Venus
Earth

Indexer to Access Individual SortedList Item

SortedList<int, string> planets = new SortedList<int, string>() {
    { 1, "Mercury" },
    { 2, "Venus" },
    { 3, "Earth" }
};

You can access the sorted list items individually by using indexer method. Key is used inside the square brackets [ ] to get the associated value.

Syntax

SortedListName[key];

Code

planets[3];
OUTPUT

Earth

The Values property is also an efficient way to retrieve values by index.

Syntax

SortedListName.Values[Index];

Code

planets.Values[1];
OUTPUT

Venus

The Keys property is an efficient way to retrieve keys by index.

Syntax

SortedListName.Keys[Index];

Code

planets.Keys[1]; // Venus key

Run Demo

OUTPUT

2

If you try to access an item with an indexer and passing a key that does not exist, an exception of type KeyNotFoundException is thrown with the message 'The given key '4' was not present in the dictionary.'.

planets[4]; // throw exception

To avoid that exception, you can use the method ContainsKey, which returns true if the key passed exists in the collection, or you can invoke the method TryGetValue, which tries to get the value but doesn’t throw an exception if key isn’t found:

// ContainsKey() method
if (planets.ContainsKey(4)) // Replace 4 with 3 
    Console.WriteLine(planets[4]);
else
    Console.WriteLine("Key not found");


// TryGetValue() method
string value = "";
if (planets.TryGetValue(4, out value)) // Replace 4 with 3
    Console.WriteLine(value);
else
    Console.WriteLine("Key not found");

Run Demo

OUTPUT

Key not found

Processing SortedList with Loops

The first statement declares a sorted list named planets that contains 3 values.

SortedList<int, string> planets = new SortedList<int, string>() {
    { 1, "Mercury" },
    { 2, "Venus" },
    { 3, "Earth" }
};

Note: It doesn’t matter where you are modifying the collection. If a collection is modified while you are enumerating its members, you get exception.

foreach Loop

You use a foreach statement to iterate through a SortedList<TKey, TValue>. Each item returned is a KeyValuePair<TKey, TValue> object, which contains a copy of the key and value elements of an item in the SortedList <TKey, TValue> collection. You can access each element through the Key and Value property.

Note: The foreach statement only allows reading from, not writing to, the collection.

foreach (var planet in planets)
    Console.WriteLine($"{planet.Key}, {planet.Value}");

Run Demo

OUTPUT

1, Mercury
2, Venus
3, Earth

You cannot use the iteration to modify the contents of the collection. Any attempt to do that will result an exception of type InvalidOperationException with the message 'Collection was modified; enumeration operation may not execute.'

foreach (var planet in planets)
{
    if (planet.Key == 2)
        planets.Remove(planet.Key); // Throw an exception here
}

Solution
Copy the keys in a temp list (or another collection) and then iterate through this temp list to modify the collection:

//Populate all the keys in a temp list
var keys = new List(planets.Keys); // Populated with 1, 2, 3

// Iterate through keys and remove the item from the collection (planets)
foreach (var key in keys)
{
    if (key == 2)
        planets.Remove(key); // Removed { 2, Venus }

    if (planets.ContainsKey(key))
        Console.WriteLine($"{key}, {planets[key]}");
}

Run Demo

OUTPUT

1, Mercury
3, Earth

for Loop

Here’s an example that shows how you can iterate through the contents of a sorted list collection using for loop:

for (int i = 0; i < planets.Count; i++)
    Console.WriteLine($"{planets.Keys[i]}, {planets.Values[i]}");

Run Demo

OUTPUT

1, Mercury
2, Venus
3, Earth

Processing SortedList With LINQ

You can use LINQ to process SortedList collection.

SortedList<int, string> planets = new SortedList<int, string>() {
    { 1, "Mercury" },
    { 2, "Venus" },
    { 3, "Earth" }
};
// Namespace required
using System.Linq;
// LINQ Method Expression
var planet = planets.Where(p => p.Key == 2).SingleOrDefault();
Console.WriteLine($"{planet.Key}, {planet.Value}");

// LINQ Query Expression
var planet = (from p in planets where p.Key == 3 select p).SingleOrDefault();
Console.WriteLine($"{planet.Key}, {planet.Value}");

Run Demo

OUTPUT

2, Venus
3, Earth

More Examples

This statement declares and initializes a sorted list of 3 objects. Once the list is created, you can add items to the list with an Add method.

SortedList<int, char> alphabet = new SortedList<int, char>() { 
    { 2, 'B' }, 
    { 1, 'A' }, 
    { 3, 'C' } 
};

//Add() to add item, auto sorted by key
alphabet.Add(4, 'D');
alphabet.Add(5, 'E');

// Add item by Key indexer
alphabet[6] = 'F';
alphabet[7] = 'G';

// Update item value associated with key 7 from G to H
alphabet[7] = 'H';

foreach (var letter in alphabet)
    Console.WriteLine($"{letter.Key}, {letter.Value}");

//Reset items
alphabet = new SortedList<int, char>() { 
    { 1, 'A' }, 
    { 2, 'B' }, 
    { 3, 'C' }, 
    { 4, 'D' }, 
    { 5, 'E' }, 
    { 6, 'F' }, 
    { 7, 'G' } 
};

//Count to get total items
Console.WriteLine(alphabet.Count); // Prints 7

//Capacity to get number of items a list can hold
Console.WriteLine(alphabet.Capacity); // Prints 8

//Keys, Values 
foreach (var key in alphabet.Keys)
    Console.WriteLine(key);

foreach (var value in alphabet.Values)
    Console.WriteLine(value);

//Get value by key indexer
Console.WriteLine(alphabet[1]); // Prints A   
Console.WriteLine(alphabet[5]); // Prints E   

// foreach loop
foreach (var letter in alphabet)
    Console.WriteLine($"{letter.Key}, {letter.Value}");

//for loop
for (int i = 0; i < alphabet.Count; i++)
    Console.WriteLine($"{alphabet.Keys[i]}, {alphabet.Values[i]}");

//ContainsValue(), ContainsKey()
Console.WriteLine(alphabet.ContainsValue('B')); // Prints True
Console.WriteLine(alphabet.ContainsKey(10)); // Prints False

//Reset items
alphabet = new SortedList<int, char>() { 
    { 1, 'A' }, 
    { 2, 'B' }, 
    { 3, 'C' }, 
    { 4, 'D' }, 
    { 5, 'E' }, 
    { 6, 'F' }, 
    { 7, 'G' } 
};

// Remove(), RemoveAt()
alphabet.Remove(2); //By Key
alphabet.RemoveAt(3); // By Index   

foreach (var letter in alphabet)
    Console.WriteLine($"{letter.Key}, {letter.Value}");

// IndexOfKey(), IndexOfValue()
alphabet.IndexOfKey(2); //By Index by Key
alphabet.IndexOfValue('B'); // Return Index by Value    

foreach (var letter in alphabet)
    Console.WriteLine($"{letter.Key}, {letter.Value}");

//Clear() to remove all the items
alphabet.Clear();
Console.WriteLine(alphabet.Count); // Prints 0

Run Demo

SortedList<int, string> Vegetables = new SortedList<int, string> {
    { 2, "Carrot" },
    { 1, "Cucumber" },
    { 3, "Tomato" },
    { 4, "Onion" },
    { 5, "Green Pepper" }
};

var integerValues = new SortedList<int, int>(3);
integerValues.Add(1, 10);
integerValues.Add(2, 20);
integerValues.Add(3, 30);

//Code that creates a sorted list to holds decimal values
SortedList<int,decimal> decimalValues = new SortedList<int,decimal>();
decimalValues[1] = 13.2m;
decimalValues[2] = 20.74m;
decimalValues[3] = 8.34m;

Run Demo

Loops

C# Reference | Microsoft Docs