对象数组的深层副本

2022-09-04 01:14:08

我想使用构造函数制作对象数组的深层副本。

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];
            }

但是,由于某种原因,我上面有的东西不起作用。我有我运行的自动化测试,但它没有通过这些测试。所以这里有一个错误,我不确定它是什么。


答案 1

您实现的是一个浅层副本。要实现深度拷贝,必须进行更改

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

将 的副本分配给 的某个事物。如何执行此操作取决于类。可能的替代方案是:other.data[i]data[i]Position

  • 复制构造函数:

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

  • 工厂方法:

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

  • 克隆:

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

笔记:

  1. 以上假设复制构造函数、工厂方法和克隆方法分别实现“right”类型的复制,具体取决于 Position 类;见下文。
  2. 该方法只有在明确支持的情况下才有效,这通常被认为是一种较差的解决方案。此外,您需要注意(即方法)的本机实现会执行浅层复制1clonePositioncloneObject.clone()

事实上,在Java中实现深度复制的一般问题很复杂。在类的情况下,人们会假设属性都是基元类型(例如int或doubles),因此深度复制与浅复制是没有意义的。但是,如果有引用属性,那么您必须依靠复制构造函数/工厂方法/克隆方法来执行所需的复制类型。在每种情况下,都需要对其进行编程。在一般情况下(您必须处理周期),这很困难,并且需要每个类实现特殊方法。Position

还有另一种可能的方法可以复制对象数组。如果数组中的对象是可序列化的,则可以通过使用复制它们,序列化和反序列化数组。然而:ObjectOutputStreamObjectInputStream

  • 这很贵,
  • 仅当对象(可传递地)可序列化时才起作用,并且
  • 不会复制任何字段的值。transient

不建议通过序列化进行复制。最好支持克隆或其他方法。

总而言之,在Java中最好避免深度复制。

最后,为了回答你关于类复制构造函数工作的问题,我希望它是这样的:Position

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

正如@Turtle所说,没有魔法。您可以实现一个构造函数(手动),该构造函数通过从现有实例复制来初始化其状态。


1 - 指定 clone() 的对象实现执行浅层复制,但可能会被覆盖。用于克隆的 javadoc 指定“合约”,如下所示:

“创建并返回此对象的副本。“复制”的确切含义可能取决于对象的类。一般的意图是,对于任何对象 x,表达式:x.clone() != x 将是 true,并且表达式: x.clone().getClass() == x.getClass() 将为 true,但这些不是绝对要求。虽然通常情况是:x.clone().equals(x)将为真,但这不是绝对的要求。

“合同”中没有任何内容涉及深度复制与浅层复制。因此,如果要在此上下文中使用 clone,则需要知道实际的类 clone 方法的行为方式。


答案 2

当你说:

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

您只是在复制引用列表(假设这是一个对象数组)。如果要创建深层副本,则需要 使用 来创建数组中每个对象的新实例。new