JRC.Collections.RedBlackTree
1.0.1
dotnet add package JRC.Collections.RedBlackTree --version 1.0.1
NuGet\Install-Package JRC.Collections.RedBlackTree -Version 1.0.1
<PackageReference Include="JRC.Collections.RedBlackTree" Version="1.0.1" />
<PackageVersion Include="JRC.Collections.RedBlackTree" Version="1.0.1" />
<PackageReference Include="JRC.Collections.RedBlackTree" />
paket add JRC.Collections.RedBlackTree --version 1.0.1
#r "nuget: JRC.Collections.RedBlackTree, 1.0.1"
#:package JRC.Collections.RedBlackTree@1.0.1
#addin nuget:?package=JRC.Collections.RedBlackTree&version=1.0.1
#tool nuget:?package=JRC.Collections.RedBlackTree&version=1.0.1
JRC.Collections.RedBlackTree
⚡ Blazing fast sorted collections with index access — what .NET should have been
🚀 30% less memory than Microsoft's SortedSet/SortedDictionary
📍 Index-based access on all collections —set[i],dict.GetAt(i),IndexOf()
⚡ O(log n) Insert/Remove for List operations
🎯 Duplicate support in sorted sets
The Problem
Ever needed to:
- Get the 5th element from a
SortedSet<T>? You can't. - Find the index of a key in
SortedDictionary<K,V>? Impossible. - Do fast Insert/Remove in the middle of a large
List<T>? O(n) kills you.
Microsoft's collections force you to choose: sorted OR indexed. Not anymore.
Features
| Feature | SortedSet | SortedDictionary | List | This Library |
|---|---|---|---|---|
| Sorted | ✅ | ✅ | ❌ | ✅ |
Index access [i] |
❌ | ❌ | ✅ | ✅ |
IndexOf() |
❌ | ❌ | O(n) | O(log n) |
Insert(i, x) |
❌ | ❌ | O(n) | O(log n) |
RemoveAt(i) |
❌ | ❌ | O(n) | O(log n) |
| Duplicates | ❌ | ❌ | ✅ | ✅ |
| Range queries | ✅ | ❌ | ❌ | ✅ |
| Memory efficient | ❌ | ❌ | ✅ | ✅ |
Installation
dotnet add package JRC.Collections.RedBlackTree
Available Classes
RedBlackTreeSet<T>
A sorted set with index access and duplicate support.
var set = new RedBlackTreeSet<Person>(
allowDuplicates: true,
(a, b) => a.Age - b.Age);
set.Add(new Person("Alice", 30));
set.Add(new Person("Bob", 25));
set.Add(new Person("Charlie", 25)); // Same age = duplicate OK
var youngest = set[0]; // O(log n) - index access!
int index = set.IndexOf(bob); // O(log n)
var aged25 = set.FindKeys(p => p.Age - 25); // All persons aged 25
RedBlackTreeSet<TItem, TSortKey>
Simplified API when sorting by a property.
var set = new RedBlackTreeSet<Person, int>(
allowDuplicates: true,
person => person.Age, // Sort key extractor
(a, b) => a - b); // Key comparer
set.Add(new Person("Alice", 30));
var person = set.FindKey(30); // Find by age directly
var range = set.FindRange(20, 30); // All persons aged 20-30
RedBlackTreeDictionary<TKey, TValue>
A sorted dictionary with index access.
var dict = new RedBlackTreeDictionary<int, string>();
dict.Add(100, "hundred");
dict.Add(50, "fifty");
dict.Add(75, "seventy-five");
var middle = dict.GetAt(1); // O(log n) - {75, "seventy-five"}
int idx = dict.IndexOfKey(75); // O(log n) - returns 1
var removed = dict.RemoveAt(0); // O(log n) - removes {50, "fifty"}
RedBlackTreeList<T>
A list with O(log n) insert/remove operations.
var list = new RedBlackTreeList<string>();
// Insert 1 million items at random positions? No problem.
for (int i = 0; i < 1_000_000; i++)
list.Insert(random.Next(list.Count), $"item{i}"); // O(log n) each!
list.RemoveAt(500_000); // O(log n)
var item = list[250_000]; // O(log n)
RedBlackTreeIndex<T>
The underlying engine behind RedBlackTreeList<T>. Use this directly if you need to build your own IList<T> implementation or want maximum control.
var index = new RedBlackTreeIndex<MyItem>();
// Add returns the internal nodeId - store it for O(log n) operations later
int nodeId = index.Add(new MyItem("data"));
// If your items track their own nodeId, you get O(log n) IndexOf
int position = index.IndexOf(nodeId); // O(log n) with nodeId!
index.Remove(nodeId); // O(log n) direct removal
// Without nodeId, you can still find items (but O(n))
int foundNodeId = index.FindNodeId(item); // O(n) scan
Pro tip: If your T stores its nodeId internally, like the default wrapper RedBlackTreeList<T> does, you can achieve O(log n) for all operations including IndexOf and Remove — something even LinkedList<T> can't do efficiently.
Linq support:
enumerable.ToRedBlackTreeList<T>(), enumerable.ToRedBlackTreeDictionary<TKey, TValue>(...), enumerable.ToRedBlackTreeSet<T>(...) and enumerable.ToRedBlackTreeSet<T, TSortedKey>(...) are provided since you are using System.Linq.
Benchmarks
Set vs SortedSet (.NET 8)
| Operation | SortedSet | RedBlackTreeSet |
|---|---|---|
| Add (random) | 44 ms | 39 ms |
| Contains | 6 ms | 10 ms |
| Enumerate | 3 ms | 3 ms |
| Range query | 2035 ms | 1539 ms |
| this[index] | ❌ | 27 ms |
| IndexOf | ❌ | 2 ms |
| RemoveAt | ❌ | 21 ms |
Dictionary vs SortedDictionary (.NET 8)
| Operation | SortedDictionary | RedBlackTreeDictionary |
|---|---|---|
| Add (sorted) | 42 ms | 34 ms |
| Add (random) | 44 ms | 39 ms |
| TryGetValue | 15 ms | 15 ms |
| Enumerate | 3 ms | 1 ms |
| Remove | 39 ms | 42 ms |
| GetAt(index) | ❌ | 30 ms |
| IndexOfKey | ❌ | 2 ms |
| RemoveAt | ❌ | 21 ms |
100,000 items, average of multiple runs
Memory Usage (1M items)
| Collection | Memory |
|---|---|
| SortedSet<int> | 40 MB |
| RedBlackTreeSet<int> | 28 MB |
30% less memory thanks to page-based allocation with contiguous arrays instead of individual heap-allocated nodes.
Serialization:
RedBlackTreeList<T>,RedBlackTreeDictionary<K, V>,RedBlackTreeSet<K>andRedBlackTreeSet<T, TSortedKey>are Binary serializable (even since .NET 8.0, where Microsoft's binary serialization is marked obsolete).RedBlackTreeList<T>,RedBlackTreeDictionary<K, V>,RedBlackTreeSet<K>andRedBlackTreeSet<T, TSortedKey>are XML serializable. Comparers and SortKeyProvider must be XML serializable too, or binary serializable (ifRedBlackTypeSerializationInfo.SubObjectsBinarySerializationAllowedis true).RedBlackTreeList<T>,RedBlackTreeDictionary<K, V>,RedBlackTreeSet<K>are Newtonsoft.Json serializable. If custom comparers are used, or ifRedBlackTreeSet<T, TSortedKey>needs to be serialized, use a converter like these ones.RedBlackTreeList<T>,RedBlackTreeDictionary<K, V>,RedBlackTreeSet<K>are System.Text.Json serializable. If custom comparers are used, or ifRedBlackTreeSet<T, TSortedKey>needs to be serialized, use a converter like these ones.RedBlackTreeList<T>,RedBlackTreeDictionary<K, V>,RedBlackTreeSet<K>are MessagePack serializable. If custom comparers are used, or ifRedBlackTreeSet<T, TSortedKey>needs to be serialized, use a converter like these ones.RedBlackTreeList<T>,RedBlackTreeDictionary<K, V>,RedBlackTreeSet<K>are Protobuf serializable. If custom comparers are used, or ifRedBlackTreeSet<T, TSortedKey>needs to be serialized, use a converter like these ones.
ℹ️ Info:
RedBlackTreeList<T>requires no comparer and is serializable in all formats without additional converters.
Technical Notes
This library is based on Microsoft's internal RBTree<K> class from System.Data, with significant improvements:
- Successor chain: O(1) enumeration via linked successor pointers (vs O(log n) tree traversal) for
RedBlackTreeList<K>(andRedBlackTreeIndex<K>), and alsoRedBlackTreeDictionary<K, V>() - Simplified API & Optimized Performance: Unlike Microsoft's shared
RBTree<K>implementation used across multiple collection types, this library provides dedicated classes with specialized optimizations for each use case, resulting in cleaner APIs and better performance.
Node Structure
┌────────────────────────────────────────────┐
│ LeftId (4 bytes) │
│ RightId (4 bytes) │
│ ParentId (4 bytes) │
│ LinkId (4 bytes) ─► Successor or Next │
│ SubTreeSize (4 bytes) │
│ Color (1 byte) │
│ Value (variable) │
└────────────────────────────────────────────┘
Acknowledgments
- Microsoft for their internal
RBTree<K>implementation inSystem.Datawhich served as the foundation for this library - Claude (Anthropic) — an excellent pair programming partner for algorithm design, optimization, and thorough testing
License
MIT License - see LICENSE for details.
Contributing
Issues and PRs welcome! If you find a bug or have a feature request, please open an issue.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net35 is compatible. net40 was computed. net403 was computed. net45 is compatible. net451 was computed. net452 was computed. net46 was computed. net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 is compatible. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETFramework 3.5
- No dependencies.
-
.NETFramework 4.5
- No dependencies.
-
.NETFramework 4.8
- No dependencies.
-
.NETStandard 2.0
- No dependencies.
-
net6.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.1 | 286 | 12/18/2025 |