Sorted collections

edit

Sorted list

SortedList<T> keeps its items sorted as you add them, accepts duplicates, and gives access by index:

var scores = new SortedList<int>();
scores.Add(42);
scores.Add(7);
scores.Add(42);
scores.Add(19);

// Items are kept sorted, duplicates included, and can be read by index
Assert.Equal([7, 19, 42, 42], scores);
Assert.Equal(7, scores[0]);
Assert.Equal(2, scores.FirstIndexOf(42));
Assert.Equal(3, scores.LastIndexOf(42));
  • Add inserts the item at its sorted position. Pass an IComparer<T> to the constructor to change the order.
  • AddRange adds many items at once. Each call to Add moves the items that follow the new one, so prefer AddRange to add many items:
var list = new SortedList<int> { 5, 1 };

// Sorts the new items once, then merges them with the existing items
list.AddRange([4, 2, 5, 3]);

Assert.Equal([1, 2, 3, 4, 5, 5], list);

Not to be confused with System.Collections.Generic.SortedList<TKey, TValue>, which is a dictionary.

Skip list

SkipList<T> keeps unique items sorted:

var tags = new SkipList<string>(StringComparer.OrdinalIgnoreCase);
Assert.True(tags.Add("beta"));
Assert.True(tags.Add("alpha"));

// Values are unique for the comparer
Assert.False(tags.Add("ALPHA"));
Assert.Equal(["alpha", "beta"], tags);

// Get the stored value that is equal to another one
Assert.True(tags.TryGetValue("BETA", out var stored));
Assert.Equal("beta", stored);
  • Add returns false when an equal item is already present, for the comparer of the list.
  • TryGetValue returns the stored item that is equal to the one you pass, for instance to get its original case.
  • Remove removes an item; the constructors can take an initial collection.

B-tree

BTree<T> also keeps unique items sorted, in a B-tree:

var tree = new BTree<int>();
Assert.True(tree.Add(30));
Assert.True(tree.Add(10));
Assert.True(tree.Add(20));
Assert.False(tree.Add(10)); // Already present

Assert.True(tree.Contains(20));
Assert.Equal([10, 20, 30], tree);

Assert.True(tree.Remove(20));
Assert.False(tree.Remove(20)); // Not present anymore
Assert.Equal([10, 30], tree);
  • Add returns false when the item is already present, and Remove returns false when it is missing.
  • Clear removes all the items.
  • Enumerating the tree returns the items in order. Changing the tree while enumerating it makes the enumerator throw an InvalidOperationException.