0

Both of my lists are being modified and am trying to avoid this behaviour...

What's the gotcha here?

I have a 'large' list and I want to remove all items that are present in the the itemsToRemoveList but without having it modify the original list.

I've simplified the example code..

List<string> aList = new List<string>(){"My","name","is", "jeff"}; // cached full list

List<string> bList = aList; // initial copy

List<string> itemsToRemoveList = new List<string>(){"jeff"};

bList.RemoveAll(itemsToRemoveList.Contains); // remove only from copy

foreach (string s in aList)
{
    Console.Write(s + " "); // expect "my name is jeff"
}
Console.WriteLine();
foreach (string s in bList)
{
    Console.Write(s + " "); // expect "my name is"
}
// however this modifies both collections. 

        Console.ReadLine();
Mdev
  • 453
  • 8
  • 19
  • 2
    That's not a "copy" that's just creating another reference to the same list. – juharr Mar 11 '16 at 12:27
  • Here's a very useful link that covers the best way to do it if your list is of value type or reference type: http://stackoverflow.com/questions/222598/how-do-i-clone-a-generic-list-in-c – sr28 Mar 11 '16 at 12:42
  • @sr28 That deals with create a new list were the items are cloned or copies of the other list. That's not really needed here since `string` is immutable. – juharr Mar 11 '16 at 12:49

6 Answers6

8

When you do

List<string> bList = aList;

That is not creating a new list. It's just setting your bList variable to a reference that points at the same list as aList does. The way to make a copy is to actually create a new list.

List<string> bList = new List<string>(aList);

But if you also want to filter values it's better to use Linq.

List<string> aList = new List<string>(){"My","name","is", "jeff"}; 
List<string> itemsToRemoveList = new List<string>(){"jeff"};
List<string> bList = aList.Where(a => !itemsToRemoveList.Contains(a)).ToList(); 

foreach (string s in aList)
{
    Console.Write(s + " "); 
}
Console.WriteLine();
foreach (string s in bList)
{
    Console.Write(s + " "); 
}
juharr
  • 31,741
  • 4
  • 58
  • 93
0

Your problem is that you are just referencing on the same Object. To avoid this behaviour, you have to initialise list B like this:

List<string> bList = new List<string>(aList);
Jonas_Hess
  • 1,874
  • 1
  • 22
  • 32
0

Remember that objects in .Net are reference types and when you assign one reference type to another, you are actually assigning a pointer to the location in memory of the original object.

List<string> aList = new List<string>(){"My","name","is", "jeff"}; // cached full list
List<string> bList = aList; // <- NOT a copy, this is assigning the same object

You mentioned that you have a rather large list. If you mean thousands or less, then juharr's answer is great. If you have millions of items in your list, you might want to rethink how that list was populated in the first place and perhaps modify your database queries.

ohiodoug
  • 1,493
  • 1
  • 9
  • 12
0

You can use LINQ Except:

List<int> a = new List<int>{ 1, 2 };
List<int> b = new List<int> { 2 };
var c = a.Except(b);
user5677774
  • 81
  • 1
  • 1
  • 6
0

You are getting this behavior because you are just referencing your original list. Your second list is just pointing to the same memory address as your original. You need to add something like the following:

    List<string> aList = new List<string>() { "My", "name", "is", "jeff" }; // cached full list

    List<string> bList = new List<string>();

    foreach(var item in aList)
    {
        bList.add(item);
    }

    List<string> itemsToRemoveList = new List<string>() { "jeff" };

    bList.RemoveAll(itemsToRemoveList.Contains); // remove only from copy

    foreach (string s in aList)
    {
        Console.Write(s + " "); // expect "my name is jeff"
    }
        Console.WriteLine();
    foreach (string s in bList)
    {
        Console.Write(s + " "); // expect "my name is"
    }
    // however this modifies both collections. 

    Console.ReadLine();

This would create a true copy of the original list and accomplish the behavior you are looking for.

James Gardner
  • 486
  • 2
  • 7
0

In your code:

List<string> bList = aList;

Does not copy the list, but rather copies the reference to the list. This is why when you are changing one list, the change reflects in the other, as they are actually the same list.

To create a new list you need to use:

List<string> bList = new List<string>(aList);

See List Reference for more details.

Also make sure you understand the difference between value and reference types in C#. When you assign a reference type you are just copying the reference, whereas when you assign a value type you copy the data. Only primitive types, structures and enums are value types in C#, everything else is reference type. In your example you are dealing with a list object which is a reference type, but you expect it to behave like a value type. Read more here.

ovidiu
  • 13
  • 3