Stashbox by Peter Csajtai

<PackageReference Include="Stashbox" Version="3.6.2-preview-635" />

 HashTree<TKey, TValue>

sealed class HashTree<TKey, TValue>
using System.Runtime.CompilerServices; namespace Stashbox.Utils.Data { internal sealed class HashTree<TKey, TValue> { private class Node { public readonly int storedHash; public readonly TKey storedKey; public TValue storedValue; public Node left; public Node right; public int height; public ExpandableArray<TKey, TValue> collisions; public Node(TKey key, TValue value, int hash) { storedValue = value; storedKey = key; storedHash = hash; height = 1; } } private Node root; public void Add(TKey key, TValue value, bool byRef = true) { root = Add(root, key, byRef ? RuntimeHelpers.GetHashCode(key) : key.GetHashCode(), value, byRef); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public TValue GetOrDefaultByValue(TKey key) { if (root == null) return default(TValue); Node node = root; int hashCode = key.GetHashCode(); while (node != null && node.storedHash != hashCode) { node = ((hashCode < node.storedHash) ? node.left : node.right); } if (node == null || !object.Equals(key, node.storedKey)) { if (node?.collisions != null) return node.collisions.GetOrDefaultByValue(key); return default(TValue); } return node.storedValue; } private static int CalculateHeight(Node node) { if (node.left != null && node.right != null) return 1 + ((node.left.height > node.right.height) ? node.left.height : node.right.height); if (node.left == null && node.right == null) return 1; return 1 + (node.left?.height ?? node.right.height); } private static int GetBalance(Node node) { if (node.left != null && node.right != null) return node.left.height - node.right.height; if (node.left == null && node.right == null) return 0; return node.left?.height ?? (node.right.height * -1); } private static Node RotateLeft(Node node) { Node right = node.right; Node left = right.left; right.left = node; node.right = left; right.height = CalculateHeight(right); node.height = CalculateHeight(node); return right; } private static Node RotateRight(Node node) { Node left = node.left; Node right = left.right; left.right = node; node.left = right; left.height = CalculateHeight(left); node.height = CalculateHeight(node); return left; } private static Node Add(Node node, TKey key, int hash, TValue value, bool byRef) { if (node == null) return new Node(key, value, hash); if (node.storedHash == hash) { CheckCollisions(node, key, value, byRef); return node; } if (node.storedHash > hash) node.left = Add(node.left, key, hash, value, byRef); else node.right = Add(node.right, key, hash, value, byRef); node.height = CalculateHeight(node); int balance = GetBalance(node); if (balance >= 2) { if (GetBalance(node.left) == -1) { node.left = RotateLeft(node.left); node = RotateRight(node); } else node = RotateRight(node); } if (balance <= -2) { if (GetBalance(node.right) == 1) { node.right = RotateRight(node.right); node = RotateLeft(node); } else node = RotateLeft(node); } return node; } private static void CheckCollisions(Node node, TKey key, TValue value, bool byRef) { if ((byRef && (object)key == (object)node.storedKey) || (!byRef && object.Equals(key, node.storedKey))) node.storedValue = value; else { if (node.collisions == null) node.collisions = new ExpandableArray<TKey, TValue>(); node.collisions.Add(new KeyValue<TKey, TValue>(key, value)); } } } }