C# SortedSet Examples

Set collection

SortedSet is an ordered set collection. You have many elements you need to store, and you want to store them in a sorted order and also eliminate all duplicates from the data structure. The SortedSet type, which is part of the System.Collections.Generic namespace in the C# language and .NET Framework, provides this functionality.

These C# examples use the SortedSet type, which represents an optimized set collection.

Add and remove elements

First, this program creates a new, empty SortedSet instance upon the managed heap. Then, it adds four elements to the set with Add; next, it removes one element with Remove. Finally, it uses the SortedSet instance (set) in the foreach loop construct to loop through all the elements. They are stored alphabetically.

Foreach Loop Examples
Program that adds and removes elements [C#]

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
	// Created sorted set of strings.
	SortedSet<string> set = new SortedSet<string>();

	// Add four elements.
	set.Add("perls");
	set.Add("net");
	set.Add("dot");
	set.Add("sam");

	// Remove an element.
	set.Remove("sam");

	// Print elements in set.
	foreach (string val in set) // <-- Alphabetical order.
	{
	    Console.WriteLine(val);
	}
    }
}

Output

dot
net
perls

Create SortedSet from List

List type.

Often you may want to create a SortedSet instance from the elements in another collection, such as an array or List. You can do this by calling the SortedSet constructor and passing the original collection reference to it. The SortedSet will then contain all those elements. Notice in the example that the duplicate element ("perls") is only present once in the SortedSet.

List Examples
Program that uses SortedSet constructor [C#]

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
	// Create list with elements.
	List<string> list = new List<string>();
	list.Add("sam");
	list.Add("allen");
	list.Add("perls");
	list.Add("perls");

	// Created sorted set from list.
	SortedSet<string> set = new SortedSet<string>(list);

	// Display contents.
	foreach (string val in set)
	{
	    Console.WriteLine(val);
	}
    }
}

Output

allen
perls
sam

RemoveWhere

Sometimes you may need to remove all elements from your SortedSet that match a certain condition. You can invoke the RemoveWhere method, with a predicate function argument, to do this elegantly. In this example, four names are added to the SortedSet that start with the letter s; we then call the RemoveWhere method with a lambda function that is used as a predicate condition. When the method returns true, the element is removed.

Lambda Expression Predicate Type
Program that uses RemoveWhere method [C#]

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
	// Create sorted set.
	SortedSet<string> set = new SortedSet<string>();
	set.Add("sam");
	set.Add("sally");
	set.Add("sandra");
	set.Add("steve");
	set.Add("mark");
	set.Add("mark");

	// Remove all elements where first letter is "s".
	set.RemoveWhere(element => element.StartsWith("s"));

	// Display.
	foreach (string val in set)
	{
	    Console.WriteLine(val);
	}
    }
}

Output

mark

Add method return value

Return keyword

With the SortedSet collection, you call the Add method to put additional elements into the set. The Add method returns a boolean value that tells us whether or not a new element was added. If it returns true, a new element was added. If it returns false, the element already existed in the set and was not added again. No exceptions are thrown if the element already exists.

Program that checks result of Add [C#]

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
	// Test add method.
	SortedSet<string> set = new SortedSet<string>();
	bool a = set.Add("sam");
	bool b = set.Add("sam");
	bool c = set.Add("sam");
	bool d = set.Add("mike");

	Console.WriteLine("{0} {1} {2} {3}", a, b, c, d);
    }
}

Output

True False False True

Count and Clear

.NET Framework information

As with other collections in the .NET Framework, you can use the Count property and the Clear() method on the SortedSet type. Please notice that the Count property can only be read, not assigned to. After the Clear() method is called, the Count property will always return zero.

Program that uses Count and Clear [C#]

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
	SortedSet<string> set = new SortedSet<string>();
	set.Add("a");
	set.Add("z");
	set.Add("a"); // Not successful.

	Console.WriteLine(set.Count);
	set.Clear();
	Console.WriteLine(set.Count);
    }
}

Output

2
0

UnionWith

The UnionWith instance method on the SortedSet type returns the union of two collections. Therefore, if the set contains A, B, and C, and the second collection contains C and D, the union will be equal to A, B, C, and D. Essentially, the UnionWith method adds all the elements into one collection. No duplicates will be found in the SortedSet.

Program that uses UnionWith method [C#]

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
	SortedSet<string> set = new SortedSet<string>();
	set.Add("a");
	set.Add("z");
	set.Add("x");

	List<string> list = new List<string>();
	list.Add("a");
	list.Add("y");

	// Union the two collections.
	set.UnionWith(list);

	// Enumerate.
	foreach (string val in set)
	{
	    Console.WriteLine(val);
	}
    }
}

Output

a
x
y
z

SymmetricExceptWith

The SymmetricExceptWith method returns all the elements in the two collections that are found in only one collection and not both collections. So if both collections contain the letter A, then the SymmetricExceptWith method will remove the letter A from the SortedSet instance. Notice how the SymmetricExceptWith method modified the SortedSet in-place; you do not need to copy the variable reference.

Program that calls SymmetricExceptWith [C#]

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
	SortedSet<string> set = new SortedSet<string>();
	set.Add("a");
	set.Add("z");
	set.Add("x");

	List<string> list = new List<string>();
	list.Add("a");
	list.Add("y");

	// Determine symmetric set.
	set.SymmetricExceptWith(list);

	// Display elements.
	foreach (string val in set)
	{
	    Console.WriteLine(val);
	}
    }
}

Output

x
y
z

ExceptWith

The ExceptWith method simply removes all the elements found in the selected collection from the SortedSet instance. The resulting SortedSet will contain all its original elements except those that were found in the other collection. This essentially gives you a way to subtract another collection's elements from the SortedSet.

Program that demonstrates ExceptWith [C#]

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
	SortedSet<string> set = new SortedSet<string>();
	set.Add("a");
	set.Add("z");
	set.Add("x");

	List<string> list = new List<string>();
	list.Add("a");
	list.Add("x");

	// Call ExceptWith to remove elements.
	set.ExceptWith(list);

	// Display elements.
	foreach (string val in set)
	{
	    Console.WriteLine(val);
	}
    }
}

Output

z

Overlaps

The Overlaps method returns a boolean that tells us whether the target collection has any elements in common with the SortedSet. Even if only one element is found in common, the result will be True. If no elements are in common, the result will be False.

Program that uses Overlaps method [C#]

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
	SortedSet<string> set = new SortedSet<string>();
	set.Add("a");
	set.Add("z");
	set.Add("x");

	List<string> list = new List<string>();
	list.Add("a");
	list.Add("y");

	HashSet<string> hash = new HashSet<string>();
	hash.Add("v");
	hash.Add("u");

	bool a = set.Overlaps(list); // Do set and list overlap? Yes.
	bool b = set.Overlaps(hash); // Do set and hash overlap? No.

	Console.WriteLine(a);
	Console.WriteLine(b);
    }
}

Output

True
False

IntersectWith

The IntersectWith method on the SortedSet changes the set instance so that it contains only the elements that were present in both collections. This means the IntersectWith method will always yield a SortedSet that has an equal number or fewer elements than before. It helps you find the "common ground" of two collections.

Program that calls IntersectWith method [C#]

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
	var set = new SortedSet<string>();
	set.Add("a");
	set.Add("z");
	set.Add("x");

	var list = new List<string>();
	list.Add("a");
	list.Add("x");

	// Compute intersection.
	set.IntersectWith(list);

	// Enumerate.
	foreach (string val in set)
	{
	    Console.WriteLine(val);
	}
    }
}

Output

a
x

Min and Max

The SortedSet contains elements that are stored in ascending sorted order. This makes it trivial for the set to compute its lowest and highest (Min and Max) element values: it simply returns the first or last elements. You cannot use an indexer [ ] with the SortedSet, so these properties are useful.

Program that demonstrates Min and Max properties [C#]

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
	// Three-element set.
	var set = new SortedSet<string>();
	set.Add("a");
	set.Add("z");
	set.Add("x");

	// Write Min and Max.
	Console.WriteLine(set.Min);
	Console.WriteLine(set.Max);
    }
}

Output

a
z

Subsets and supersets (proper)

The SortedSet also provides several methods that compute subsets and supersets: the IsSubsetOf, IsSupersetOf, IsProperSubsetOf, and IsProperSupersetOf methods. A subset is contained entirely inside another set; a superset contains another set entirely. Proper subsets and proper supersets are the same as regular ones except they cannot have the same number of elements; they must have at least one fewer. Visit the Wolfram link for information on set theory and proper subsets.

Proper Subset
Program that uses IsSubsetOf, IsSupersetOf... [C#]

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
	var set = new SortedSet<string>();
	set.Add("a");
	set.Add("z");
	set.Add("x");

	var list1 = new List<string>();
	list1.Add("a");
	list1.Add("z");
	list1.Add("x");

	var list2 = new List<string>();
	list2.Add("a");
	list2.Add("z");
	list2.Add("x");
	list2.Add("y");

	Console.WriteLine("IsProperSubsetOf: {0}", set.IsProperSubsetOf(list1));
	Console.WriteLine("IsSubsetOf: {0}", set.IsSubsetOf(list1));

	Console.WriteLine("IsProperSubsetOf: {0}", set.IsProperSubsetOf(list2));
	Console.WriteLine("IsSubsetOf: {0}", set.IsSubsetOf(list2));

	var list3 = new List<string>();
	list3.Add("a");
	list3.Add("z");

	Console.WriteLine("IsProperSupersetOf: {0}", set.IsProperSupersetOf(list3));
	Console.WriteLine("IsSupersetOf: {0}", set.IsSupersetOf(list3));
    }
}

Output

IsProperSubsetOf: False
IsSubsetOf: True
IsProperSubsetOf: True
IsSubsetOf: True
IsProperSupersetOf: True
IsSupersetOf: True

SetEquals

How can you determine if another collection contains the same elements as your SortedSet? You can use the SetEquals method. In my testing, I found that the SetEquals method treats the second collection as though it has no duplicate elements; if duplicate elements are present, the two collections may still be considered equal.

Program that demonstrates SetEquals method [C#]

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
	var set = new SortedSet<string>();
	set.Add("s");
	set.Add("a");
	set.Add("m");

	var list = new List<string>();
	list.Add("s");
	list.Add("a");
	list.Add("m");

	// See if the two collections contain the same elements.
	bool a = set.SetEquals(list);
	Console.WriteLine(a);
    }
}

Output

True

GetViewBetween

How you you see what elements a SortedSet instance contains between and including two values? You can use the GetViewBetween method with two arguments: the minimum value, and the maximum value. Then, you can loop over the SortedSet that is returned in a foreach-loop, or do further operations as needed.

Program that calls GetViewBetween method [C#]

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
	var set = new SortedSet<int>();
	set.Add(5);
	set.Add(7);
	set.Add(8);
	set.Add(9);
	set.Add(4);

	// Call GetViewBetween method.
	SortedSet<int> view = set.GetViewBetween(4, 7);

	foreach (int val in view)
	{
	    Console.WriteLine(val);
	}
    }
}

Output

4
5
7

Contains

If you need to determine if your SortedSet has a certain element in it, you could use a foreach-loop and imperatively scan all the elements. However, the Contains method is more effective in that it is shorter and less prone to errors in most cases. It simply returns true or false depending on whether the element was located.

Program that demonstrates Contains method [C#]

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
	var set = new SortedSet<int>();
	set.Add(8);
	set.Add(7);
	set.Add(8);
	set.Add(9);
	set.Add(3);

	// Use Contains method.
	bool a = set.Contains(9);
	Console.WriteLine(a);
    }
}

Output

True

Reverse

Reverse graphic

When you add elements to your SortedSet, they are automatically stored in ascending order: lowest to highest values. However, you can call the Reverse method to return the elements in the opposite, reverse order. If you want to loop over your SortedSet from highest to lowest (reverse alphabetical order), you can call Reverse in a foreach-loop as shown here.

Program that uses Reverse method on SortedSet [C#]

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
	var set = new SortedSet<int>();
	set.Add(8);
	set.Add(7);
	set.Add(8);
	set.Add(9);
	set.Add(3);

	foreach (int val in set)
	{
	    Console.Write(val);
	    Console.Write(' ');
	}
	Console.WriteLine();

	// Use Reverse.
	foreach (int val in set.Reverse())
	{
	    Console.Write(val);
	    Console.Write(' ');
	}
	Console.WriteLine();
    }
}

Output

3 7 8 9
9 8 7 3

Performance

Question and answer

Is the SortedSet collection efficient to use? This depends of course on your program. However, the SortedSet does not include hashing, meaning that it has to do linear searches for lookups. The SortedSet is therefore much slower than the HashSet for most cases where you need to do lookups. It would be many times slower on large sets.

Internally, the SortedSet is implemented as a tree with a Root node, and a Left and Right node on every node instance. This is the typical implementation of a binary tree. Every node will need to be allocated on the managed heap, which would also impact performance in a negative way. In my experience, for most programs a hashing mechanism (such as that found in Dictionary and HashSet) is superior for performance.

Dictionary Examples

Summary

The C# programming language

The SortedSet collection provides a variety of useful functionality. In some cases, it can replace confusing, custom types with a simple type directly from the .NET Framework. This reduces program size and complexity, and also minimizes bugs and the attack surface. It represents an interesting combination of the HashSet and the SortedList collections.

HashSet Programs SortedList Collection Collections
.NET