0

I'm using dictionaries in C#, and I've spent a couple of hours figuring out why my program doesn't work, and the reason is that when I manipulate a copy of a dictionary I made, then these manipulations also affect the original dictionary for some reason.

I've boiled my problem down to the following example:

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        Dictionary<int, List<int>> D = new Dictionary<int, List<int>>();
        List<int> L1 = new List<int>(){ 1, 2, 3 };
        List<int> L2 = new List<int>() { 4, 5, 6 };
        D.Add(1,L1);
        D.Add(2,L2);
        Dictionary<int, List<int>> Dcopy = new Dictionary<int, List<int>>(D);
        Dcopy[1].Add(4);
    }
}

In this code, when I add an element to the list corresponding to key 1 in the copy, this element also appears in the original dictionary.

When I search online, it seem to have something to do with "reference types", and the recommended fix always seem to involve a code similar to

Dictionary<int, List<int>> Dcopy = new Dictionary<int, List<int>>(D);

which I did include in my program, but for some reason, this does not work.

Any suggestions as to why it doesn't work in my case, and any advice on what to do instead?

Best regards.

HowDoICSharply
  • 547
  • 1
  • 7
  • 20

3 Answers3

6

You're doing a shallow copy, instead of a deep copy. You basically need to iterate through your dictionary and create new lists

var Dcopy = new Dictionary<int, List<int>>();
foreach (var entry in D)
{
    Dcopy.Add(entry.Key, new List<int>(entry.Value));
} 

Or you can use the following Linq instead of the foreach

var DCopy = D.ToDictionary(entry => entry.Key, entry => new List<int>(entry.Value));

Since your lists contains int which is a value type you do not need to "clone" deeper than the lists. If instead the list contained reference types then you'd have to additionally clone them as well and possibly any reference properties all the way down.

juharr
  • 31,741
  • 4
  • 58
  • 93
  • 4
    Note, if the list was holding something that was mutable (a class with properties you could set) you would also need to make copies of each item in the list too (also making copies of any mutable properties, and properties of properties, and so on...). – Scott Chamberlain Nov 02 '15 at 21:52
  • Thanks. I understand how it behaves now, but am confused as to why it was build to behave this way. In unfortunate circumstances, where you have a complicated nesting of reference types (which I will read about to understand better) or of "mutable items", you would have to carefully copy each thing in the nesting and patch them together to get a structure that is genuinely distinct from the original? – HowDoICSharply Nov 02 '15 at 22:05
  • 1
    @HowDoICSharply For a case like this you can write a fairly simple code to do the deep copy. If you are working with a more complicated nested structure then you'd probably want to check out the question that Alexie linked in the comments. – juharr Nov 02 '15 at 22:08
4

The dictionary copy constructor makes a shallow copy of the dictionary. Since the values are lists (reference types) the lists are not cloned. If you want to make a deep copy you need to clone the values as well:

    Dictionary<int, List<int>> Dcopy = D.ToDictionary(kvp => kpv.Key,
                                                      kvp => kvp.Value.ToList());

Since the values are collection of value types then there's no need to close the contents of the list - cloning the list itself is sufficient. Same for the keys - they are value types so no cloning is necessary.

D Stanley
  • 149,601
  • 11
  • 178
  • 240
-1

If you don't need a deep copy of the lists, why not simply use .NET provided constructor, avoiding extension calls?

Dictionary<int, List<int>> Dcopy = new Dictionary<int,List<int>>(D);
gisi
  • 1