Created
January 6, 2025 18:23
-
-
Save theodorzoulias/d83840004075983f664b0951e3a0f8dd to your computer and use it in GitHub Desktop.
ConcurrentMultiDictionary
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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