1

My problem is that I need to copy a list but a deep copy. When I modify the list a, I don't want to modify the list b. I use JDK11 so I could use list.copyOf but using that when I modify a, b is also modified. Am I doing something wrong?

b = List.copyOf(a);
System.out.println("A start:" + b.get(2).getSeating());
System.out.println("B start:" + b.get(2).getSeating());
a.get(2).setSeating(27);
System.out.println("Hi there" );
System.out.println("A end:" + a.get(2).getSeating());
System.out.println("B end:" + b.get(2).getSeating());

The output of that assignation:

The output of that assignation

frogatto
  • 28,539
  • 11
  • 83
  • 129
Alex Ferre
  • 21
  • 1
  • 2
  • 1
    *"Am I doing something wrong?"* Yes, you're incorrect believing that `List.copyOf()` is a deep copy. The javadoc doesn't say anything about deep copy, so why do you believe that? – Andreas Feb 24 '19 at 11:28
  • Returns an unmodifiable List containing the elements of the given Collection, in its iteration order. The given Collection must not be null, and it must not contain any null elements. If the given Collection is subsequently modified, the returned List will not reflect such modifications. – Alex Ferre Feb 24 '19 at 11:42
  • Yes, that is what the javadoc says. Nowhere does it say it's doing a deep copy. And it is copying the *references* to the elements in the list, not the elements themselves. The copied *list* is unmodifiable. The elements are not, and they are shared between the old list and the new list. That is what a shallow copy means. So I ask again, why do you believe that `copyOf` is doing a **deep** copy? Or should I instead ask whether you know what a deep copy is? – Andreas Feb 24 '19 at 11:46
  • I know what a deep copy means but i think that java should make an easier way to do that, cause sometimes it is important to have the original to modify the elements and the original in case you want the original values. – Alex Ferre Feb 24 '19 at 13:20
  • Java does have an easy way to do this, the `clone()` method, but only *you* can determine how to correctly copy an object, i.e. which fields need to be deep-copied, so *you* need to tell Java the detail of how to do it. – Andreas Feb 24 '19 at 20:02

1 Answers1

1

The copyOf method does not make a deep copy, according to the documentation it returns a non-mutable view, i.e. a read-only list. It does not make copies of the elements in the list. To make a deep copy you would basically need to iterate through the collection and deep-copy the elements one by one into a new collection. This can be tricky in the general case. Some objects support clone, others have custom copy methods, but in the general case Java has no deep copy methods that always works.

In short the solution differs depending on what you have in your list. As it seems to be custom objects from your application it is probably easiest to just iterate through the list and copy instances to a new list with a copy constructor or clone method that your provide in your class.

ewramner
  • 5,810
  • 2
  • 17
  • 33
  • When you say clone method how would you make it?, cause i find the same problem which is that i keep references to the objects of the list in the new list – Alex Ferre Feb 24 '19 at 11:39
  • @AlexFerre Read the duplicate link. It's there for a reason. – Andreas Feb 24 '19 at 11:48
  • @Andreas the other answer i think is not enoguh good cause it does not give an example of how to override a clone method. And i repeat i think that having to override a method to have a simple copy of an array should be regulated by java. It is to tricky and complicated for noobs as me – Alex Ferre Feb 24 '19 at 13:24
  • 1
    Deep copy is tricky and there is no good standard solution. See https://www.baeldung.com/java-deep-copy for a few alternatives including clone. Personally I would recommend using a copy constructor (i.e. a constructor that takes another similar object and copies all the fields) if you control the source code. That way you can copy some of the fields deeply and some with a straight reference copy depending on requirements. – ewramner Feb 24 '19 at 16:33
  • 1
    @AlexFerre *"the other answer [...] does not give an example of how to override a clone method"*. Sure it does. Look at the [second answer](https://stackoverflow.com/a/7042287/5221149). It is not a great example, but it surely is an example. – Andreas Feb 24 '19 at 20:07
  • @Andreas actually it is a very bad example as the method should have been named copy to avoid confusion. It does not override clone and doesn't really use clone at all, rather it uses a normal constructor and sets fields manually. That is fine (as noted above it is my preferred solution) but it is not clone. To use clone, implement the marker interface Cloneable and override the Object#clone method, call super.clone() to get the new instance and optionally replace other fields that need to be deep-copied (i.e. mutable fields). – ewramner Feb 25 '19 at 07:54
  • @ewramner It is valid to implement `clone()` that way, though if you do, you should make the class `final`. – Andreas Feb 25 '19 at 17:57
  • Sure it is a possible solution, but I would not use it as an example of how to override the clone method. The method should have been called copy as that is what it does. Using clone kind of assumes that the object is copied with the native clone method and in this case it is not. So yes, it works and provided that the class is declared final there is nothing wrong with it, but it is not a good example of how to use clone and Cloneable in my view. Don't want to enter an argument about it though, I'll leave it at that! – ewramner Feb 26 '19 at 08:07