Skip to content

Instantly share code, notes, and snippets.

@theodorzoulias
Created January 6, 2025 18:23
Show Gist options
  • Save theodorzoulias/d83840004075983f664b0951e3a0f8dd to your computer and use it in GitHub Desktop.
Save theodorzoulias/d83840004075983f664b0951e3a0f8dd to your computer and use it in GitHub Desktop.
ConcurrentMultiDictionary
// https://stackoverflow.com/questions/60695167/concurrentdictionary-with-multiple-values-per-key-removing-empty-entries
// Alternative implementation
using System.Collections;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Collections.Immutable;
using System.Linq;
public class ConcurrentMultiDictionary<TKey, TValue>
: IEnumerable<KeyValuePair<TKey, TValue[]>>
{
private readonly ConcurrentDictionary<TKey, ImmutableHashSet<TValue>> _dictionary;
public ConcurrentMultiDictionary()
{
_dictionary = new ConcurrentDictionary<TKey, ImmutableHashSet<TValue>>();
}
public int Count => _dictionary.Count;
public bool Add(TKey key, TValue value)
{
while (true)
{
if (_dictionary.TryGetValue(key, out var existing))
{
var updated = existing.Add(value);
if (ReferenceEquals(updated, existing)) return false;
if (_dictionary.TryUpdate(key, updated, existing)) return true;
}
else
{
if (_dictionary.TryAdd(key, ImmutableHashSet.Create(value))) return true;
}
}
}
public bool Remove(TKey key, TValue value)
{
while (true)
{
if (_dictionary.TryGetValue(key, out var existing))
{
var updated = existing.Remove(value);
if (ReferenceEquals(updated, existing)) return false;
if (updated.IsEmpty)
{
if (_dictionary.TryRemove(KeyValuePair.Create(key, existing))) return true;
}
else
{
if (_dictionary.TryUpdate(key, updated, existing)) return true;
}
}
else
{
return false;
}
}
}
public bool TryGetValues(TKey key, out TValue[] values)
{
if (_dictionary.TryGetValue(key, out var set))
{
values = set.ToArray();
return true;
}
values = null;
return false;
}
public bool Contains(TKey key, TValue value)
{
if (_dictionary.TryGetValue(key, out var set))
{
return set.Contains(value);
}
return false;
}
public KeyValuePair<TKey, TValue[]>[] ToArray()
{
return _dictionary
.ToArray()
.Select(e => new KeyValuePair<TKey, TValue[]>(e.Key, e.Value.ToArray()))
.ToArray();
}
public IEnumerator<KeyValuePair<TKey, TValue[]>> GetEnumerator()
{
return _dictionary
.Select(e => new KeyValuePair<TKey, TValue[]>(e.Key, e.Value.ToArray()))
.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment