views:

127

answers:

3

I want to make a deep copy of an object array using a constructor.

public class PositionList {
    private Position[] data = new Position[0];

public PositionList(PositionList other, boolean deepCopy) {
        if (deepCopy){
            size=other.getSize();
            data=new Position[other.data.length];
            for (int i=0;i<data.length;i++){
            data[i]=other.data[i];
            }

However, what I have above for some reason is not working. I have automated tests that I run, and its failing those tests. So theres an error an here that Im not sure what it is.

+2  A: 

When you say:

data[i]=other.data[i];

You are just copying a list of references (assuming this is an array of objects). If you want to make a deep copy, you need to use new to create a new instance of each object in the array.

Justin Ethier
But where would I use new? In the loop?
fprime
Yes. In each iteration, create a `new` object and add it to `data`.
Justin Ethier
Ok Im confused. So would I remove `data=new Position[other.data.length];` for the code I have above and somehow add it into the loop?
fprime
+1  A: 

Instead of saying:

data[i]=other.data[i]

You will want to make a copy constructor for Position (in other words, a constructor for Position that takes in another Position and copies the primitive data inside it) and say data[i]=new Position(other.data[i]);

Basically your "deep copy" constructor the PositionList is a copy constructor, although copy constructor does tend to indicate a deep copy, so the deepCopy parameter is unnecessary.

Thomas
-1 clone makes a shallow copy not a deep copy. It can end up working OK for primitives and immutable objects, but don't confuse it with a deep copy.
bwawok
We're supposed to do it without clone...
fprime
@bwawok: that's true, I was assuming that `Position` was a POJO, and thus, `.clone()` would work.
Thomas
How can I do this without clone?
fprime
@fprime: Then use a copy constructor, as my alternate solution said. That said, I really should have put the "copy constructor" solution first, due to the fact that `.clone()` does have drawbacks.
Thomas
+1  A: 

What you have implemented is a shallow copy. To implement a deep copy, you must change

data[i] = other.data[i];

to some thing that assigns a copy of other.data[i] to data[i]. How you do this depends on the Position class. Possible alternatives are:

  • a copy constructor:

    data[i] = new Position(other.data[i]);

  • a factory method:

    data[i] = createPosition(other.data[i]);

  • clone:

    data[i] = (Position) other.data[i].clone();

Notes:

  1. The above assume that the copy constructor, factory method and clone method respectively implement the "right" kind of copying, depending on the Position class; see below.
  2. The clone approach will only work if Position explicitly supports it, and this is generally regarded as an inferior solution. Besides, you need to be aware that the native implementation clone does a shallow copy.

In fact the general problem of implementing deep copying in Java is complicated. In the case of the Position class, one would assume that the attributes are all primitive types (e.g. ints or doubles), and therefore a deep versus shallow copying is moot. But if there are reference attributes, then you have to rely on the copy constructor / factory method / clone method to do the kind of copying that you require. In each case it needs to be programmed in. And in the general case (where you have to deal with cycles) it is difficult and requires each class to implement special methods.

All in all, deep copying is best avoided in Java.

Finally, to answer your question about the Position classes copy constructor works, I expect it is something like this:

public class Position {
    private int x;
    private int y;
    ...
    public Position(Position other) {
        this.x = other.x;
        this.y = other.y;
    }
    ...
}

As @Turtle says, there's no magic involved. You implement a copy constructor by hand.

Stephen C
The copy constructor is the one I need. The one you have above worked perfectly, but I dont understand it. So we're creating a new Position, but what is this argument that we feed it, other.data[i]? Can you describe how that works?
fprime
@fprime: It's not magic. Someone needs to write a constructor for the Position class which takes an instance of that class as its only argument and returns a deep copy.
Turtle