37

I have a ConcurrentDictionary object that I would like to set to a Dictionary object.

Casting between them is not allowed. So how do I do it?

JYelton
  • 35,664
  • 27
  • 132
  • 191
umbersar
  • 1,821
  • 3
  • 24
  • 34

6 Answers6

51

The ConcurrentDictionary<K,V> class implements the IDictionary<K,V> interface, which should be enough for most requirements. But if you really need a concrete Dictionary<K,V>...

var newDictionary = yourConcurrentDictionary.ToDictionary(kvp => kvp.Key,
                                                          kvp => kvp.Value,
                                                          yourConcurrentDictionary.Comparer);

// or...
// substitute your actual key and value types in place of TKey and TValue
var newDictionary = new Dictionary<TKey, TValue>(yourConcurrentDictionary, yourConcurrentDictionary.Comparer);
ANeves
  • 6,219
  • 3
  • 39
  • 63
LukeH
  • 263,068
  • 57
  • 365
  • 409
  • 7
    Note that the dictionary to copy might use a non-default `IEqualityComparer` that won't be preserved that way! Better: `var newDict = dict.ToDictionary(kvp => kvp.Key, kvp => kvp.Value, dict.Comparer);` – Roman Reiner Dec 01 '14 at 16:12
  • 2
    Note that [MSDN](https://msdn.microsoft.com/en-us/library/system.collections.concurrent(v=vs.110).aspx) says this may not be thread safe. How would you make it thread safe? – JonDrnek May 05 '15 at 12:48
  • Don't worry, it actually is thread-safe, by virtue of the ConcurrentDictionary. You will get a snapshot of the ConcurrentDictionary's content. The dictionary you get from that won't be thread-safe itself later on though. – Falanwe Mar 10 '16 at 11:08
  • 5
    @Falanwe: It is safe, but you don't get a snapshot of the contents: _"The enumerator returned from the dictionary is safe to use concurrently with reads and writes to the dictionary, however it does not represent a moment-in-time snapshot of the dictionary. The contents exposed through the enumerator may contain modifications made to the dictionary after GetEnumerator was called."_ (From the _Remarks_ section of https://msdn.microsoft.com/en-us/library/dd287131.aspx) – LukeH Mar 10 '16 at 12:50
22

Why do you need to convert it to a Dictionary? ConcurrentDictionary<K, V> implements the IDictionary<K, V> interface, is that not enough?

If you really need a Dictionary<K, V>, you can copy it using LINQ:

var myDictionary = myConcurrentDictionary.ToDictionary(entry => entry.Key,
                                                       entry => entry.Value);

Note that this makes a copy. You cannot just assign a ConcurrentDictionary to a Dictionary, since ConcurrentDictionary is not a subtype of Dictionary. That's the whole point of interfaces like IDictionary: You can abstract away the desired interface ("some kind of dictionary") from the concrete implementation (concurrent/non-concurrent hashmap).

Heinzi
  • 167,459
  • 57
  • 363
  • 519
14

I think i have found a way to do it.

ConcurrentDictionary<int, int> concDict= new ConcurrentDictionary<int, int>( );
Dictionary dict= new Dictionary<int, int>( concDict);
umbersar
  • 1,821
  • 3
  • 24
  • 34
2
ConcurrentDictionary<int, string> cd = new ConcurrentDictionary<int, string>();
Dictionary<int,string> d = cd.ToDictionary(pair => pair.Key, pair => pair.Value);
Andrey
  • 20,487
  • 26
  • 108
  • 176
1

If you only need to guarantee thread-safety, but not a moment-in-time snapshot, use concurrentDictionary.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);.

This is because .ToDictionary() is simply the LINQ implementation that operates over IEnumerable. The ConcurrentDictionary enumerator is thread-safe, but the values can change underneath you as the enumerator is enumerated.

If you need to guarantee a moment-in-time snapshot, use concurrentDictionary.ToArray().ToDictionary(kvp => kvp.Key, kvp => kvp.Value);.

.ToArray() is implemented by the ConcurrentDictionary itself, and uses an internal lock to guarantee a moment-in-time snapshot of the dictionary contents. You may then do whatever you like with the resultant array, such as creating a new dictionary from its values.

Ryan
  • 1,670
  • 18
  • 25
1

Converting a ConcurrentDictionary<K,V> to a Dictionary<K,V> has a couple of gotchas.

  1. The two dictionaries should have the same Comparer. Otherwise two keys that are different according to the one comparer might be equal according to the other comparer, in which case attempting to insert the second key in the target dictionary will cause a run-time ArgumentException with the message "An item with the same key has already been added."

  2. Although in practice a ConcurrentDictionary<K,V> emits unique keys when it is enumerated, this is not guaranteed by the documentation, and the Microsoft engineers have explicitly stated that the observed behavior is not intentional. So there is nothing that prevents them from changing the current implementation in a future .NET version, in a way that will allow the emission of duplicate keys during a single enumeration of a ConcurrentDictionary<K,V> instance. This could cause again the appearance of run-time ArgumentExceptions, if you use the Dictionary<K,V> constructor that has a collection or dictionary parameter. If you want to be on the safe side, it's a good idea to be defensive, and insert the key-value pairs in the Dictionary<K,V> with either the TryAdd method or the set indexer. The TryAdd will preserve the first occurrence of the duplicate key, while the set indexer will preserve the last.

/// <summary>
/// Creates a Dictionary from a ConcurrentDictionary.
/// </summary>
public static Dictionary<TKey, TValue> ToDictionary<TKey, TValue>(
    this ConcurrentDictionary<TKey, TValue> source, bool snapshot = false)
{
    ArgumentNullException.ThrowIfNull(source);
    if (snapshot)
        return new Dictionary<TKey, TValue>(source.ToArray(), source.Comparer);

    // Create a Dictionary without snapshot semantics.
    Dictionary<TKey, TValue> result = new(source.Comparer);
    foreach (var (key, value) in source)
        result.TryAdd(key, value); // or result[key] = value;
    return result;
}
Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104