【问题标题】:Deep copy of an object array对象数组的深拷贝
【发布时间】:2011-04-26 05:21:06
【问题描述】:

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

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

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

【问题讨论】:

    标签: java arrays constructor object copy


    【解决方案1】:

    当你说:

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

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

    【讨论】:

    • 但是我应该在哪里使用 new 呢?在循环中?
    • 是的。在每次迭代中,创建一个new 对象并将其添加到data
    • 好吧,我很困惑。那么我会为上面的代码删除data=new Position[other.data.length]; 并以某种方式将其添加到循环中吗?
    【解决方案2】:

    而不是说:

    data[i]=other.data[i]
    

    您需要为 Position 创建一个复制构造函数(换句话说,一个 Position 的构造函数接受另一个 Position 并复制其中的原始数据)并说 data[i]=new Position(other.data[i]);

    基本上你的“深拷贝”构造函数PositionList是一个拷贝构造函数,虽然拷贝构造函数确实倾向于表示一个深拷贝,所以deepCopy参数是不必要的。

    【讨论】:

    • -1 克隆生成浅拷贝而不是深拷贝。它最终可以适用于基元和不可变对象,但不要将其与深层副本混淆。
    • @bwawok:没错,我假设Position 是一个POJO,因此.clone() 可以工作。
    • 没有克隆怎么办?
    • @fprime:然后使用复制构造函数,正如我的替代解决方案所说。也就是说,我真的应该把“复制构造函数”解决方案放在第一位,因为.clone() 确实有缺点。
    【解决方案3】:

    您实施的是副本。要实现深度副本,您必须 改变

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

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

    还有另一种潜在方法可以复制对象数组。如果数组中的对象是serializable,那么你可以通过使用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() 的 Object 实现执行浅拷贝,但这可能会被覆盖。 clone 的 javadoc 指定“合同”如下:

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

    “合同”中没有提到深拷贝和浅拷贝。所以如果你打算在这个上下文中使用clone,你需要知道clone方法的实际类的行为。

    【讨论】:

    • 复制构造函数是我需要的。你上面的那个工作得很好,但我不明白。所以我们正在创建一个新位置,但是我们提供给它的参数是什么,other.data[i]?你能描述一下它是如何工作的吗?
    • @fprime:这不是魔法。有人需要为 Position 类编写一个构造函数,该构造函数将该类的实例作为其唯一参数并返回一个深层副本。
    • 不一定 clone() 做深拷贝:例如,返回这个 ArrayList 实例的浅拷贝。 (元素本身不会被复制。)
    • 关于第三个选项,clone() - 确保 Point 实现 Cloneable
    • 1) No. 2) 鉴于没有复制Object 的通用方法,因此不可能“深度复制”任意Object[]
    【解决方案4】:

    这应该是一个“深”的副本

    int [] numbers = { 2, 3, 4, 5};
    
    int [] numbersClone = (int[])numbers.clone();
    

    【讨论】:

    • 这适用于基元数组(如整数),但问题是关于对象数组。
    【解决方案5】:

    这是我使用的一个函数:

    function copy(arr) {
      return arr
        .map(x => Object
          .keys(x)
          .reduce((acc, y) => {
            acc[y] = x[y]
            return acc
          }, {}))
    }
    

    它仅适用于具有单层对象的数组。

    【讨论】:

      猜你喜欢
      • 2015-09-14
      • 2015-11-28
      • 2011-12-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-26
      • 2012-06-19
      • 2019-11-17
      相关资源
      最近更新 更多