private HashTree <K, V> RemoveOrUpdate(int hash, K key, IsUpdated <V> isUpdated = null, bool ignoreKey = false) { if (Height == 0) { return(this); } HashTree <K, V> result; if (hash == Hash) // found matched Node { if (ignoreKey || Equals(Key, key)) { if (!ignoreKey) { V updatedValue; if (isUpdated != null && isUpdated(Value, out updatedValue)) { return(new HashTree <K, V>(Hash, Key, updatedValue, Conflicts, Left, Right)); } if (Conflicts != null) { if (Conflicts.Length == 1) { return(new HashTree <K, V>(Hash, Conflicts[0].Key, Conflicts[0].Value, null, Left, Right)); } var shrinkedConflicts = new KV <K, V> [Conflicts.Length - 1]; Array.Copy(Conflicts, 1, shrinkedConflicts, 0, shrinkedConflicts.Length); return(new HashTree <K, V>(Hash, Conflicts[0].Key, Conflicts[0].Value, shrinkedConflicts, Left, Right)); } } if (Height == 1) // remove node { return(Empty); } if (Right.IsEmpty) { result = Left; } else if (Left.IsEmpty) { result = Right; } else { // we have two children, so remove the next highest node and replace this node with it. var successor = Right; while (!successor.Left.IsEmpty) { successor = successor.Left; } result = successor.With(Left, Right.RemoveOrUpdate(successor.Hash, default(K), ignoreKey: true)); } } else if (Conflicts != null) { var index = Conflicts.Length - 1; while (index >= 0 && !Equals(Conflicts[index].Key, key)) { --index; } if (index == -1) // key is not found in conflicts - just return { return(this); } V updatedValue; var conflict = Conflicts[index]; if (isUpdated != null && isUpdated(conflict.Value, out updatedValue)) { var updatedConflicts = new KV <K, V> [Conflicts.Length]; Array.Copy(Conflicts, 0, updatedConflicts, 0, updatedConflicts.Length); updatedConflicts[index] = new KV <K, V>(conflict.Key, updatedValue); return(new HashTree <K, V>(Hash, Key, Value, updatedConflicts, Left, Right)); } if (Conflicts.Length == 1) { return(new HashTree <K, V>(Hash, Key, Value, null, Left, Right)); } var shrinkedConflicts = new KV <K, V> [Conflicts.Length - 1]; var newIndex = 0; for (var i = 0; i < Conflicts.Length; ++i) { if (i != index) { shrinkedConflicts[newIndex++] = Conflicts[i]; } } return(new HashTree <K, V>(Hash, Key, Value, shrinkedConflicts, Left, Right)); } else { return(this); // if key is not matching and no conflicts to lookup - just return } } else if (hash < Hash) { result = With(Left.RemoveOrUpdate(hash, key, isUpdated, ignoreKey), Right); } else { result = With(Left, Right.RemoveOrUpdate(hash, key, isUpdated, ignoreKey)); } return(result.KeepBalanced()); }
/// <summary>Removes or updates value for specified key, or does nothing if key is not found. /// Based on Eric Lippert http://blogs.msdn.com/b/ericlippert/archive/2008/01/21/immutability-in-c-part-nine-academic-plus-my-avl-tree-implementation.aspx </summary> /// <param name="key">Key to look for.</param> <param name="isUpdated">(optional) Delegate to update value, return true from delegate if value is updated.</param> /// <returns>New tree with removed or updated value.</returns> public HashTree <K, V> RemoveOrUpdate(K key, IsUpdated <V> isUpdated = null) { return(RemoveOrUpdate(key.GetHashCode(), key, isUpdated)); }