Deep copy of an object array
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) {
开发者_JAVA技巧 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.
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:
- 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.
- The
clone
approach will only work ifPosition
explicitly supports it, and this is generally regarded as an inferior solution. Besides, you need to be aware that the native implementation ofclone
(i.e. theObject.clone()
method) does a shallow copy1.
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.
There is one other potential way to copy an array of objects. If the objects in the array are serializable, then you can copy them by using ObjectOutputStream
and ObjectInputStream
serialize and then deserialize the array. However:
- this is expensive,
- it only works if the objects are (transitively) serializable, and
- the values of any
transient
fields won't be copied.
Copying by serialization is not recommended. It would be better to support cloning or some other method.
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 constructor (by hand) that initializes its state by copying from an existing instance.
1 - It is specified that the Object implementation of clone()
does a shallow copy, but this may be overridden. The javadoc for clone
specifies the "contract" as follows:
"Creates and returns a copy of this object. The precise meaning of "copy" may depend on the class of the object. The general intent is that, for any object x, the expression:
x.clone() != x
will be true, and that the expression:x.clone().getClass() == x.getClass()
will be true, but these are not absolute requirements. While it is typically the case that:x.clone().equals(x)
will be true, this is not an absolute requirement."
Nothing in the "contract" talks about deep versus shallow copying. So if you are going to use clone
in this context, you need to know how the actual classes clone
method behaves.
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.
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.
Here is a function I use:
function copy(arr) {
return arr
.map(x => Object
.keys(x)
.reduce((acc, y) => {
acc[y] = x[y]
return acc
}, {}))
}
It only works on arrays with objects with a single level.
This should make a "deep" copy
int [] numbers = { 2, 3, 4, 5};
int [] numbersClone = (int[])numbers.clone();
精彩评论