0

Is there any way to swap key and value in Concurrent Dictionary in C# ? I know for Dictionary type, it can be something like

dictionary = dictionary.ToDictionary(x => x.Value, x => x.Key);

Anything similar in ConcurrentDictionary?

superninja
  • 3,114
  • 7
  • 30
  • 63
  • 1
    All `ConcurrentDictionary` does is add some locks around the add/remove/access operations. And of coruse MT only stuff, like TryAdd and TryUpdate - also with locks. – Christopher Nov 16 '19 at 01:39
  • Do you want to do this swap while the `ConcurrentDictionary` is actively updated by multiple threads? If yes, then you probably want to take a snapshot of the data contained in the `ConcurrentDictionary` before doing the swap, and not use a mix of new and old entries. To take a snapshot you need to use its method [`ToArray`](https://learn.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentdictionary-2.toarray). Don't use LINQ methods ([reference](https://stackoverflow.com/questions/41038514/calling-tolist-on-concurrentdictionarytkey-tvalue-while-adding-items)). – Theodor Zoulias Nov 16 '19 at 10:48
  • Note: The caveat above is specific to the `ConcurrentDictionary` class. Other concurrent containers, like the `ConcurrentQueue` for example, can be safely used with LINQ because their `GetEnumerator` method uses a snapshot of the data. ([citation](https://learn.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentqueue-1.getenumerator#remarks)) – Theodor Zoulias Nov 16 '19 at 11:40
  • @Christopher so if all I'm doing is read access for the dictionary, is there a need to use Concurrent Dictionary? – superninja Nov 20 '19 at 00:57
  • You use the concurrent classes - or your own locking mechanism - if you work with the same instance from multple tasks/threads. You did not show us *nearly* enough for a answer. In the end, only a person it front of the code can prevent race conditions. – Christopher Nov 20 '19 at 15:25

1 Answers1

1

You cannot cast or convert Dictionary to ConcurrentDictionary directly. Buy you can create a ConcurrentDictionary with a Dictionary, check this constructor

public ConcurrentDictionary(IEnumerable<KeyValuePair<TKey, TValue>> collection);

And look the extension method of LINQ that you say

  public static Dictionary<TKey, TElement> ToDictionary<TSource, TKey, TElement>(
        this IEnumerable<TSource> source,
        Func<TSource, TKey> keySelector,
        Func<TSource, TElement> elementSelector);

So if Dictionary has compatibility wiht IEnumerable<KeyValuePair<TKey, TValue>>

Solution 1

Just use ToDictionary extension method of LINQ

var concurrentDict = new ConcurrentDictionary<string, string>();
/* Add some items */
bool firstItem = concurrentDict.TryAdd("1", "First");  //returns true
bool secondItem = concurrentDict.TryAdd("2", "Second");  //returns  true

/* Swaping Value <-> Key to new Dictionary */
Dictionary<string,string> normalDict = concurrentDict.ToDictionary(x => x.Value, x => x.Key);

/* creating ConcurrentDictionary */
var newConcurrentDict = new ConcurrentDictionary<string, string>(normalDict);

Solution 2

What about thread safe?

You can use a custom extension method (like LINQ)

var concurrentDict = new ConcurrentDictionary<string, string>();
/* Add some items */
bool firstItem = concurrentDict.TryAdd("1", "First");  //returns true
bool secondItem = concurrentDict.TryAdd("2", "Second");  //returns  true

/* Swaping Value <-> Key to new Dictionary */
Dictionary<string,string> normalDict = concurrentDict.ToDictionary(x => x.Value, x => x.Key);

/* creating ConcurrentDictionary */
var newConcurrentDict = normalDict.ToConcurrentDictionary();

Extension method

Ref https://stackoverflow.com/a/27064366/1669574

public static class ConcurrentDictionaryExtensions
{
    public static ConcurrentDictionary<TKey, TElement> ToConcurrentDictionary<TSource, TKey, TElement>(
        this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector,
        IEqualityComparer<TKey> comparer)
    {
        if (source == null) throw new ArgumentNullException("source");
        if (keySelector == null) throw new ArgumentNullException("keySelector");
        if (elementSelector == null) throw new ArgumentNullException("elementSelector");

        ConcurrentDictionary<TKey, TElement> d = new ConcurrentDictionary<TKey, TElement>(comparer);
        foreach (TSource element in source)
            d.TryAdd(keySelector(element), elementSelector(element));

        return d;
    }

    public static ConcurrentDictionary<TKey, TSource> ToConcurrentDictionary<TSource, TKey>(
        this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
    {
        return ToConcurrentDictionary<TSource, TKey, TSource>(source, keySelector, IdentityFunction<TSource>.Instance,
            null);
    }

    public static ConcurrentDictionary<TKey, TSource> ToConcurrentDictionary<TSource, TKey>(
        this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
    {
        return ToConcurrentDictionary<TSource, TKey, TSource>(source, keySelector, IdentityFunction<TSource>.Instance,
            comparer);
    }

    public static ConcurrentDictionary<TKey, TElement> ToConcurrentDictionary<TSource, TKey, TElement>(
        this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector)
    {
        return ToConcurrentDictionary<TSource, TKey, TElement>(source, keySelector, elementSelector, null);
    }

    internal class IdentityFunction<TElement>
    {
        public static Func<TElement, TElement> Instance
        {
            get { return x => x; }
        }
    }
}
Clystian
  • 155
  • 1
  • 13