5

我正在尝试克隆 MyGraph 的一个对象,我希望它是一个深层副本,因此对象内的数组列表也被克隆。现在我有:

public static MyGraph deepCopy(MyGraph G){
    MyGraph Copy = (MyGraph) G.clone();

    Copy.VertexG = (ArrayList<Integer>) G.VertexG.clone();
    Copy.EdgeG = (ArrayList<String>) G.EdgeG.clone();

    return Copy;
}

当它试图克隆 arraylist 时,这将返回一个错误。我不确定这是否是将数组列表添加到对象的正确方法。

4

4 回答 4

3

中的clone操作ArrayList返回对象的浅拷贝,并且不适合您的目的。手动解决方法是:

  1. 创建与源列表大小相同的目标数组列表
  2. 迭代源列表并将每个项目的克隆创建到目标列表中

显然,这仅在数组列表包含实现clone的项目时才有效,并且项目clone操作实际上返回一个深层副本。换句话说,它不能保证。实际上,为 Java 对象实现深度克隆功能并不容易,请参阅Java 中的广泛讨论:深度克隆/复制实例和其他 SO 线程的推荐解决方案,以了解可用选项。除了那里提供的答案外,还有其他一些选项:

序列化

如果层次结构中的所有(必需的)对象都可以序列化,那么您可以使用这个简单的代码进行深度克隆:

public MyGraph deepCopy() {
    try {
        final ByteArrayOutputStream baos = new ByteArrayOutputStream(256);
        final ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(this);
        oos.close();

        final ObjectInputStream ois = new ObjectInputStream(
                new ByteArrayInputStream(baos.toByteArray()));
        final MyGraph clone = (QuicksortTest) ois.readObject();
        return clone;
    } catch (final Exception e) {
        throw new RuntimeException("Cloning failed");
    }
}

请注意,一些深度克隆库将标准 Java 序列化与反射黑客和/或字节码检测结合起来,以使整个对象层次结构完全可序列化。您可能需要,也可能不需要。

复制工具

例如,Dozer提供快速的深拷贝功能。Orika也可以达到相同的效果,尽管需要更多配置:

public MyGraph deepCopy() {
    final DozerBeanMapper mapper = new DozerBeanMapper();
    final QuicksortTest clone = mapper.map(this, MyGraph.class);
    return clone;
}

当然,唯一的缺点是您需要将额外的依赖项拉入您的项目。

总而言之,您的deepCopy方法不应该静态的。此外,您应该认真考虑通过将对象的状态设为私有并实现 getter/setter 来封装对象的状态。

于 2013-03-01T10:38:09.567 回答
0

您调用的每个类clone()都必须实现该Cloneable接口。从您的评论中,我了解您的MyGraph课程没有实现该Cloneable接口。在这种情况下,Object.clone()抛出CloneNotSupportedException.

于 2013-03-01T10:58:20.143 回答
0

尝试进行深度复制cloning很复杂,因为您需要确保所有类都实现Cloneable接口并且它们具有clone()定义。

更好的方法是通过Copy Constructor or Serialization. 这是我详细讨论过的博客。希望能帮助到你 :)

于 2013-03-01T11:05:20.033 回答
0

Java中克隆的一个基本概念问题[可以说基本问题]是一个类型的字段可能List<String>代表至少五个非常不同的事物:

  • 唯一现存的对可变列表的引用,用于封装其可变状态,但作为唯一现存的引用 - 不会封装其身份(该列表可以替换为包含相同项目的不同列表,不改变程序的语义)。包含此字段的对象的正确克隆将包含对包含相同项目的不同列表的引用。

  • 对可变列表的引用,虽然它允许自己被变异,但永远不会暴露给任何实际会改变它的东西。当其他代码不会改变列表或将其暴露给可能这样做的代码时,才能与其他代码共享此引用。包含此字段的对象的正确克隆可以包含对原始列表或包含相同项目的不同列表的引用。

  • 对不可变列表的引用。此引用可以与其他代码自由共享,而无需考虑该代码如何公开它。如上所述,包含此字段的对象的正确克隆可以包含对原始列表或副本的引用。

  • 对某个其他对象拥有的可变列表的引用,该列表的保留是为了将其绑定到封装在列表中的其他对象状态的那些方面。持有该字段的对象的正确克隆必须持有对同一列表的引用,而不是其副本。

  • 对这个对象拥有的可变列表的引用,但其他对象也有一个引用,目的是观察这个对象的状态,或者向这个对象提供信息。拥有该字段的对象不能单独正确克隆,尽管可以克隆一组相互连接的对象,并为新的对象集提供一组与原始组中的对象同构的互连。

字段所引用的对象的具体类型可以区分上述一些情况,但不能区分所有情况。特别是,第一个和第四个场景需要克隆方法的不同行为,尽管在这两个场景中引用可能指向一个ArrayList<string>.

于 2013-03-06T17:05:59.433 回答