Skip to content

Instantly share code, notes, and snippets.

@TheBigNeo
Last active May 20, 2026 13:49
Show Gist options
  • Select an option

  • Save TheBigNeo/a2af79d1674314856032bc165c41c1ed to your computer and use it in GitHub Desktop.

Select an option

Save TheBigNeo/a2af79d1674314856032bc165c41c1ed to your computer and use it in GitHub Desktop.
ConcurrentHashSet
#nullable enable
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using JetBrains.Annotations;
namespace Utils;
/// <summary>
/// Represents a thread-safe set of values that can be accessed by multiple threads concurrently.<br />
/// A set is a collection that contains no duplicate elements and whose elements are in no particular order.
/// </summary>
/// <seealso cref="HashSet{T}" />
/// <typeparam name="T">The type of elements in the hash set.</typeparam>
[DebuggerDisplay("Count = {" + nameof(Count) + "}")]
public sealed class ConcurrentHashSet<T> : IReadOnlyCollection<T>, ICollection<T>
where T : notnull
{
private const byte DummyByte = byte.MinValue;
/// <summary>
/// The underlying dictionary used to store elements.
/// </summary>
private readonly ConcurrentDictionary<T, byte> _dictionary;
/// <summary>
/// Returns the number of elements in the set.
/// </summary>
/// <remarks>
/// To check for empty, use <see cref="IsEmpty" />. It has better performance.
/// </remarks>
public int Count => _dictionary.Count;
/// <inheritdoc />
public bool IsReadOnly => false;
/// <summary>
/// Returns if the set is empty.
/// </summary>
public bool IsEmpty => _dictionary.IsEmpty;
/// <summary>
/// Initializes a new instance of the <see cref="ConcurrentHashSet{T}" /> class that is empty.
/// </summary>
public ConcurrentHashSet()
{
_dictionary = new ConcurrentDictionary<T, byte>();
}
/// <inheritdoc />
public ConcurrentHashSet(ConcurrentHashSet<T> hashSet)
: this(hashSet ?? throw new ArgumentNullException(nameof(hashSet)), hashSet.Count)
{
}
/// <inheritdoc />
public ConcurrentHashSet(HashSet<T> hashSet)
: this(hashSet ?? throw new ArgumentNullException(nameof(hashSet)), hashSet.Count)
{
}
/// <inheritdoc />
public ConcurrentHashSet(IReadOnlyCollection<T> collection)
: this(collection ?? throw new ArgumentNullException(nameof(collection)), collection.Count)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ConcurrentHashSet{T}" /> class that contains elements copied from the given <paramref name="collection" />.
/// </summary>
/// <param name="collection">The <see cref="IEnumerable{T}" /> whose elements are copied to the new <see cref="ConcurrentHashSet{TKey}" />.</param>
/// <param name="capacity">The size of the <paramref name="collection" />.</param>
/// <exception cref="ArgumentNullException"><paramref name="collection" /> is <c>null</c>.</exception>
public ConcurrentHashSet(IEnumerable<T> collection, int capacity)
{
ArgumentNullException.ThrowIfNull(collection);
_dictionary = new ConcurrentDictionary<T, byte>(Environment.ProcessorCount, capacity);
foreach (T item in collection)
{
_dictionary.TryAdd(item, DummyByte);
}
}
/// <inheritdoc cref="ISet{T}.Add" />
public bool Add(T item) => _dictionary.TryAdd(item, DummyByte);
/// <inheritdoc />
void ICollection<T>.Add(T item) => Add(item);
/// <inheritdoc cref="ISet{T}.Clear" />
public void Clear() => _dictionary.Clear();
/// <inheritdoc cref="ISet{T}.Contains" />
public bool Contains(T item) => _dictionary.ContainsKey(item);
/// <inheritdoc />
public void CopyTo(T[] array, int arrayIndex)
{
ArgumentNullException.ThrowIfNull(array);
ArgumentOutOfRangeException.ThrowIfNegative(arrayIndex);
if (arrayIndex > array.Length)
{
string message = $"The {nameof(arrayIndex)} is out of bounds. Array Length: {array.Length}, Index: {arrayIndex}";
throw new ArgumentOutOfRangeException(nameof(arrayIndex), arrayIndex, message);
}
if (Count > array.Length - arrayIndex)
{
string message = $"Destination array is not long enough to copy all elements. Array Length: {array.Length}, HashSet Count: {Count}";
throw new ArgumentException(message, nameof(array));
}
foreach (T key in _dictionary.Keys)
{
array[arrayIndex++] = key;
}
}
/// <inheritdoc cref="ICollection{T}.Remove" />
public bool Remove(T item) => _dictionary.TryRemove(item, out _);
/// <inheritdoc cref="ICollection{T}.GetEnumerator" />
public IEnumerator<T> GetEnumerator() => _dictionary.Keys.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
/// <summary>
/// Creates a new list containing all elements from the set.<br />
/// The returned list is a snapshot of the elements.
/// So this is <b>not</b> a reference that will be updated if the set changes.
/// </summary>
/// <returns>A new list containing all elements from the set.</returns>
[MustUseReturnValue]
public List<T> ToList() => _dictionary.Keys.ToList();
/// <summary>
/// Determines whether the given <paramref name="other" /> has the same elements. <br />
/// Both must have the same number of elements and must contain the same elements.<br />
/// <b>The order of the elements is ignored.</b>
/// </summary>
/// <param name="other">The collection to compare with this set.</param>
/// <returns>
/// <c>true</c>, if the <paramref name="other" /> has the same elements.<br />
/// <c>false</c>, if <paramref name="other" /> is <c>null</c> or has any different element.
/// </returns>
/// <seealso cref="HashSet{T}.SetEquals" />
[MustUseReturnValue]
public bool SetEquals(IReadOnlyCollection<T>? other)
{
if (other is null)
{
return false;
}
// A set is equal to itself.
if (ReferenceEquals(other, this))
{
return true;
}
if (Count != other.Count)
{
return false;
}
return _dictionary.Keys.All(other.Contains);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment