Search code examples
c#.netcopyclonegeneric-list

How to copy a List<T> without cloning


Perhaps I am missing something trivial. I have a couple of List<T>s and I need one big list from them which is a union of all the other lists. But I do want their references in that big list and not just the values/copies (unlike many questions I typically find on SO).

For example I have this,

List<string> list1 = new List<string> { "a", "b", "c" };
List<string> list2 = new List<string> { "1", "2", "3" };

var unionList = GetThatList(list1, list2);

Suppose I get the list I want in unionList, then this should happen:

unionList.Remove("a"); => list1.Remove("a");
unionList.Remove("1"); => list2.Remove("1");

//in other words
//
//unionList.Count = 4;
//list1.Count = 2;
//list2.Count = 2;

To make it clear, this typically happens with

unionList = list1; //got the reference copy.

But how do I go about with the second list, list2 to add to unionList?

I tried Add and AddRange but they obviously clone and not copy.

unionList = list1;
unionList.AddRange(list2); //-- error, clones, not copies here.

and

foreach (var item in list2)
{
    unionList.Add(item); //-- error, clones, not copies here.
}

Update: I think I am asking something that makes no sense, and something that inherently is not possible in the language..


Solution

  • I don't think any such class exists. You could implement it yourself. Here's a start:

    class CombinedLists<T> : IEnumerable<T> // Add more interfaces here.
                                            // Maybe IList<T>, but how should it work?
    {
        private List<List<T>> lists = new List<List<T>>();
    
        public void AddList(List<T> list)
        {
            lists.Add(list);
        }
    
        public IEnumerator<T> GetEnumerator()
        {
            return lists.SelectMany(x => x).GetEnumerator();
        }
    
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    
        public bool Remove(T t)
        {
            foreach (List<T> list in lists)
            {
                if (list.Remove(t)) { return true; }
            }
            return false;
        }
    
        // Implement the other methods.
    }
    

    Here's some code you can use to test it:

    List<string> list1 = new List<string> { "a", "b", "c" };
    List<string> list2 = new List<string> { "1", "2", "3" };
    CombinedLists<string> c = new CombinedLists<string>();
    c.AddList(list1);
    c.AddList(list2);
    
    c.Remove("a");
    c.Remove("1");
    
    foreach (var x in c) { Console.WriteLine(x); }
    Console.WriteLine(list1.Count);
    Console.WriteLine(list2.Count);
    

    Removing items is fairly simple. But you might get problems if you try to insert items into your combined list. It is not always well-defined which list should receive the inserted items. For example, if you have a combined list that contains two empty lists and you insert an item at index 0, should the item be added to the first or the second empty list?