/// <summary> /// Splits the dictionary at the given node. /// </summary> /// <returns>Dictionary at the new node</returns> private C5Lib.KeyValuePair <int, IDictionary <TKey, TValue> > SplitNode( C5Lib.KeyValuePair <int, IDictionary <TKey, TValue> > node) { C5Lib.KeyValuePair <int, IDictionary <TKey, TValue> > nextNode; var nextHash = _circle.TrySuccessor(node.Key, out nextNode) ? nextNode.Key : _maxHash; var midHash = node.Key + (int)((nextHash - (long)node.Key) / 2); Debug.Assert(node.Key < nextHash && node.Key < midHash && midHash < nextHash); if (_circle.Contains(midHash)) { // TODO allow larger sub-dictionaries, to avoid exception? throw new Exception( "Run out of nodes. Hash code is not evenly distributed enough."); } // now take (ideally half) the keys from the old node and insert them into the new node var dictionary = node.Value; var entriesToMove = dictionary.Where(kv => _equalityComparer.GetHashCode(kv.Key) >= midHash) .ToList(); var emptyDictionary = new Dictionary <TKey, TValue>(LargeDictionaryFactory.MaxDictionarySize, _equalityComparer); IDictionary <TKey, TValue> dictionaryForMidNode; if (entriesToMove.Count == dictionary.Count) { // optimisation where *all* of the keys need moving _circle[node.Key] = emptyDictionary; _circle[midHash] = dictionary; dictionaryForMidNode = dictionary; } else { foreach (var entryToMove in entriesToMove) { var removed = dictionary.Remove(entryToMove.Key); Debug.Assert(removed); emptyDictionary.Add(entryToMove.Key, entryToMove.Value); } _circle[midHash] = emptyDictionary; dictionaryForMidNode = emptyDictionary; } return(new C5Lib.KeyValuePair <int, IDictionary <TKey, TValue> >(midHash, dictionaryForMidNode)); }
/// <summary> /// Initializes a new instance of the <see cref="ConsistentHashLargeDictionary{TKey, TValue}"/> class. /// </summary> /// <param name="initialCapacity">The initial capacity.</param> /// <param name="equalityComparer">The equality comparer.</param> public ConsistentHashLargeDictionary( int initialCapacity = 0, [CanBeNull] IEqualityComparer <TKey> equalityComparer = null) { _equalityComparer = equalityComparer ?? EqualityComparer <TKey> .Default; var nodeCount = initialCapacity > 0 ? (int)Math.Ceiling(initialCapacity / (double) LargeDictionaryFactory.MaxDictionarySize) : 1; // split the circle into 'nodeCount' dictionaries spread equaly apart // optimisation for small dictionaries: just create the single node at the minimum point // on the circle if (nodeCount == 1) { _circle[_minHash] = new Dictionary <TKey, TValue>(initialCapacity, equalityComparer); } else { // for larger dictionaries, create the points on the circle, but leave the creation // of each dictionary until actually need to add to it var hashGap = (_maxHash - (long)_minHash) / nodeCount; // need to use a long for the hash to avoid wrapping around on the last gap. It's safe // to cast as has is always bounded by MIN_HASH and MAX_HASH which are ints. for (long hash = _minHash; hash < _maxHash; hash += hashGap) { _circle[(int)hash] = null; } } _firstNode = _circle.First(); }