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;
}
}
}
}