How Create a New Deep Copy (Clone) of a List<T>

How create a new deep copy (clone) of a List T ?

You need to create new Book objects then put those in a new List:

List<Book> books_2 = books_1.Select(book => new Book(book.title)).ToList();

Update: Slightly simpler... List<T> has a method called ConvertAll that returns a new list:

List<Book> books_2 = books_1.ConvertAll(book => new Book(book.title));

Is there any better deep clone methods for list?

That's not a real deep-copy because the Card instances are still the same, only the list is different. You could have this much simpler:

List<Card> cloneList = cards.ToList();

You need to "copy" all properties of the Card instances as well:

public List<Card> Copy(List<Card> cards)
{
List<Card> cloneList = new List<Card>();
foreach (var card in cards)
{
Card clone = new Card();
clone.Property1 = card.Property1;
// ... other properties
cloneList.Add(clone);
}
return cloneList;
}

You could also provide a factory method that creates a clone of a given Card instance:

public class Card
{
// ...

public Card GetDeepCopy()
{
Card deepCopy = new Card();
deepCopy.Property1 = this.Property1;
// ...
return deepCopy;
}
}

Then you have encapsulated this logic in one place where you can even access private members(fields, properties, constructors). Change the line in the Copy method above to:

cloneList.Add(card.GetDeepCopy()); 

Deep copy of List T

The idiomatic way to approach this in C# is to implement ICloneable on your Data, and write a Clone method that does the deep copy (and then presumably a Enumerable.CloneRange method that can clone part of your list at once.) There isn't any built-in trick or framework method to make it easier than that.

Unless memory and performance are a real concern, I suggest that you try hard to redesign it to operate on immutable Data objects, though, instead. It'll wind up much simpler.

How do I clone a generic list in C#?

You can use an extension method.

static class Extensions
{
public static IList<T> Clone<T>(this IList<T> listToClone) where T: ICloneable
{
return listToClone.Select(item => (T)item.Clone()).ToList();
}
}

How do I clone a list so that it doesn't change unexpectedly after assignment?

new_list = my_list doesn't actually create a second list. The assignment just copies the reference to the list, not the actual list, so both new_list and my_list refer to the same list after the assignment.

To actually copy the list, you have several options:

  • You can use the builtin list.copy() method (available since Python 3.3):

    new_list = old_list.copy()
  • You can slice it:

    new_list = old_list[:]

    Alex Martelli's opinion (at least back in 2007) about this is, that it is a weird syntax and it does not make sense to use it ever. ;) (In his opinion, the next one is more readable).

  • You can use the built in list() constructor:

    new_list = list(old_list)
  • You can use generic copy.copy():

    import copy
    new_list = copy.copy(old_list)

    This is a little slower than list() because it has to find out the datatype of old_list first.

  • If you need to copy the elements of the list as well, use generic copy.deepcopy():

    import copy
    new_list = copy.deepcopy(old_list)

    Obviously the slowest and most memory-needing method, but sometimes unavoidable. This operates recursively; it will handle any number of levels of nested lists (or other containers).

Example:

import copy

class Foo(object):
def __init__(self, val):
self.val = val

def __repr__(self):
return f'Foo({self.val!r})'

foo = Foo(1)

a = ['foo', foo]
b = a.copy()
c = a[:]
d = list(a)
e = copy.copy(a)
f = copy.deepcopy(a)

# edit orignal list and instance
a.append('baz')
foo.val = 5

print(f'original: {a}\nlist.copy(): {b}\nslice: {c}\nlist(): {d}\ncopy: {e}\ndeepcopy: {f}')

Result:

original: ['foo', Foo(5), 'baz']
list.copy(): ['foo', Foo(5)]
slice: ['foo', Foo(5)]
list(): ['foo', Foo(5)]
copy: ['foo', Foo(5)]
deepcopy: ['foo', Foo(1)]

How to make a deep copy of an object with a List variable in Java?

Your copy constructors are just doing shallow copies of each field. That's fine for strings because they're immutable, and it's fine for ints because they're primitive (which means they lack identity and are immutable). In those cases, there is no important difference between shallow and deep copies. But it doesn't work in general for lists because lists can be mutable and so can their elements. So instead of just pointing at the same list, you need to make a new list and deep copy each element of the original list into the new one.

Use this helper method to make deep copies of any lists:

static <T> List<T> deepCopyList(List<T> list, UnaryOperator<T> deepCopyElement) {
return list.stream().map(deepCopyElement).collect(
Collectors.toCollection(ArrayList::new)
);
}

Like so:

public AtomDTO(AtomDTO that) {
this(that.getName(), that.getType(), deepCopyList(that.getBonds(), BondDTO::new));
}

how to make a deep copy of my list

You can create an extension method where you serialize the object only to deserialize it again. This will create a new object with it's own references, thus a Deep Copy.

Public Module Extensions
<System.Runtime.CompilerServices.Extension()> _
Public Function DeepCopy(Of T)(ByVal Obj As T) As T
If Obj.GetType().IsSerializable = False Then Return Nothing

Using MStream As New MemoryStream
Dim Formatter As New BinaryFormatter
Formatter.Serialize(MStream, Obj)
MStream.Position = 0
Return DirectCast(Formatter.Deserialize(MStream), T)
End Using
End Function
End Module

Now you can just call:

Dim network As List(Of train) = per_network.DeepCopy()

EDIT:

These are the required imports for my code above:

Imports System.IO
Imports System.Runtime.Serialization.Formatters.Binary

How can I create a deep copy of my list collection

It's getting too hard to sort this out in the comments.

  • Remove the Clone method from your Author class. It is useless.

In your book class, you have two problems to solve.

  • The constructor takes a list of authors, but the caller who passed it in might change it. If we just copy the reference, then the caller can alter accidentally the list held by the book.

  • The book gives back a list of authors. If a caller adds something to that list then again, they've mutated the book.

You can solve both problems with an immutable collection. Download the immutable collections library using NuGet if you haven't got it already.

using System.Collections.Immutable;
...
public class Book
{
public string bookTitle {get; private set;}
private ImmutableList<Author> authors;
public IReadOnlyList<Author> Authors { get { return authors; } }
public string ISBN {get; private set; }

public Book(string bookTitle, IEnumerable<Author> authors, string ISBN)
{
this.authors = ImmutableList<Author>.Empty.AddRange(authors);
this.bookTitle = bookTitle;
this.ISBN = ISBN;
}
}

There. Now you make a copy of the sequence of authors, so if the caller changes that sequence, no worries, you have a copy. And you hand out an IReadOnlyList that is implemented by an immutable collection, so no one can change it.

Couple more things. You ask "is this right?"

public class Book
{
private ReadOnlyCollection<Author> authors;
public Book(ReadOnlyCollection<Author> authors)
{
//Is it okay to do this?
this.authors = authors;
}

public List<Author> Authors
{
get
{ //Create a shallow copy
return new ReadOnlyCollection<Author>(authors);
}
}

(Extraneous stuff removed).

No, that is not quite right, for a couple reasons. First, the read only collection is just a wrapper around a mutable collection. You are still in the situation where the caller controls the underlying collection, and therefore can change it.

Second, the typing doesn't quite work out; you can't convert a ReadOnlyCollection to a List.

This is confusing I know. There is a subtle distinction here. A read only collection is just that: you can only read it. It doesn't mean that someone else cannot write it! Such a collection is still mutable, it is just not mutable by you. An immutable collection is truly immutable; no one can change it.

Next: you are doing very well by making the author and the book both immutable. But what if you want to change it? As you note, to change an immutable book means making a new book. But you already have an old book; how can you do that efficiently? The common pattern is:

public class Book
{
public string Title {get; private set;}
private ImmutableList<Author> authors;
public IReadOnlyList<Author> Authors { get { return authors; } }
public string ISBN {get; private set; }

public Book(string title, IEnumerable<Author> authors, string ISBN) : this(
title,
ImmutableList<Author>.Empty.AddRange(authors),
ISBN) {}

public Book(string title, ImmutableList<Authors> authors, string ISBN)
{
this.Title = title;
this.Authors = authors;
this.ISBN = ISBN;
}
public Book WithTitle(string newTitle)
{
return new Book(newTitle, authors, ISBN);
}
public Book WithISBN(string newISBN)
{
return new Book(Title, authors, newISBN);
}
public Book WithAuthor(Author author)
{
return new Book(Title, authors.Add(author), ISBN);
}
public static readonly Empty = new Book("", ImmutableList<Author>.Empty, "");
}

Now you can do this:

Book tlotr = Book.Empty.WithAuthor("JRRT").WithTitle("The Lord Of The Rings");

And so on.

deep clone a List of object in c#


Warning: The BinaryFormatter type is dangerous when used with untrusted input. Whilst the usage below should be safe, Microsoft recommend avoiding BinaryFormatter altogether due to its potential for misuse, and will remove it from .NET 7–8. Consider using another serializer or approach for your deep clones.

First off, you can define a utility method for deep-cloning any object (root):

public static T DeepClone<T>(T obj)
{
using (var stream = new MemoryStream())
{
var formatter = new BinaryFormatter();
formatter.Serialize(stream, obj);
stream.Position = 0;
return (T)formatter.Deserialize(stream);
}
}

If you want to deep-clone myList, all you need to do is pass it as parameter to the method above:

List<SmallClass> myListClone = DeepClone(myList);

The most important consideration you need to pay attention to is that all your classes must be marked as serializable, typically through the [SerializableAttribute].

[SerializableAttribute]
public class SmallClass
{
// …
}


Related Topics



Leave a reply



Submit