Segmented Hashset Class
Useful for input or output buffer for use in parallel invoke operations where unique values are needed.
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; public class SegmentedHashSet<T> : IEnumerable<T> { private readonly IntHs<T>[] _array; private readonly int _size; private int _length; public SegmentedHashSet(int length, int size) { _array = new IntHs<T>[4096]; for (var i = 0; i < length; ++i) _array[i] = new IntHs<T>(size); _length = length; _size = size; } public IEnumerator<T> GetEnumerator() { throw new NotImplementedException(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public void Add(T item, int index) { if (index >= _length) for (var i = _length; i <= index; ++i) { _array[i] = new IntHs<T>(_size); _length++; } if (!ContainedInArray(item)) _array[index].Add(item); } private bool ContainedInArray(T item) { for (var i = 0; i < _length; ++i) if (_array[i].Contains(item)) return true; return false; } public T[] CombineToArray() { var totalCount = 0; for (var i = 0; i < _length; ++i) totalCount += _array[i].Count; var ta = new T[totalCount]; var ptr = 0; for (var i = 0; i < _length; ++i) { var it = _array[i].ToArray(); _array[i].Zero(); for (var j = 0; j < it.Length; ++j) ta[ptr++] = it[j]; } return ta; } [DebuggerDisplay("Count = {Count}")] internal class IntHs<T> : IEnumerable { private Bucket[] _buckets; internal IEqualityComparer<T> Comparer; public IntHs(int size) : this(size, null) { } public IntHs(int size, IEqualityComparer<T> comparer) { if (comparer == null) comparer = EqualityComparer<T>.Default; Comparer = comparer; _buckets = new Bucket[size]; Count = 0; } public int Count { get; private set; } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public void Zero() { _buckets = null; Count = 0; } public bool Add(T item) { if (Count >= _buckets.Length) EnsureSize(); var hashCode = Comparer.GetHashCode(item) & int.MaxValue; var apos = FindEntry(item, hashCode).APos; if (apos != -1) return false; var pos = hashCode % _buckets.Length; if (_buckets[pos] == null) _buckets[pos] = new Bucket(); _buckets[pos].Add(item); Count++; return true; } public T[] ToArray() { var newArray = new T[Count]; using (var en = GetEnumerator()) { var ptr = 0; while (en.MoveNext()) { var value = en.Current; if (value == null) break; newArray[ptr++] = value; } return newArray; } } private (int APos, int BPos) FindEntry(T item, int hashCode) { if (Count == 0) return (-1, -1); var aPos = hashCode % _buckets.Length; var bPos = 0; if (_buckets[aPos] == null) { _buckets[aPos] = new Bucket(); return (-1, -1); } foreach (var i in _buckets[aPos].Values) { if (Equals(i, item)) return (aPos, bPos); bPos++; } return (-1, -1); } private void EnsureSize() { var cArray = ToArray(); _buckets = new Bucket[_buckets.Length + _buckets.Length / 4 * 3]; foreach (var i in cArray) { var hashCode = Comparer.GetHashCode(i) & int.MaxValue; var pos = hashCode % _buckets.Length; if (_buckets[pos] == null) _buckets[pos] = new Bucket(); _buckets[pos].Add(i); } } public bool Contains(T item) { var hashCode = Comparer.GetHashCode(item) & int.MaxValue; return FindEntry(item, hashCode).APos != -1; } public IEnumerator<T> GetEnumerator() { return GetEnum(); } public IEnumerator<T> GetEnum() { for (var i = 0; i < _buckets.Length; i++) if (_buckets[i] != null) for (var j = 0; j < _buckets[i].Count; ++j) yield return _buckets[i].Values[j]; } internal class Bucket { public int Count; public T[] Values; public Bucket() { Values = new T[1]; Count = 0; } public void Add(T item) { if (Count >= Values.Length) Array.Resize(ref Values, Values.Length + 1); Values[Count++] = item; } } } }