对象Java的深层副本

时间:2013-03-01 10:08:05

标签: java clone

我正在尝试克隆MyGraph的一个对象,我希望它是一个深层复制,因此对象内的arraylists也被克隆。现在我有:

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时会返回错误。我不确定这是否是将arraylists添加到对象的正确方法。

4 个答案:

答案 0 :(得分:3)

clone中的ArrayList操作会返回对象的浅表副本,并不适合您的目的。手动解决方法是:

  1. 创建与源列表大小相同的目标数组列表
  2. 迭代源列表并创建每个项目的克隆到目标列表
  3. 显然,这仅在数组列表包含实现clone的项目时才有效,此外项目clone操作实际上返回深层副本。换句话说,它不能保证。实际上,为Java对象实现深度克隆功能并不容易,请参考Java: recommended solution for deep cloning/copying an instance和其他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来封装对象的状态。

答案 1 :(得分:0)

您调用clone()的每个类都必须实现Cloneable接口。根据您的评论,我了解您的MyGraph类未实现Cloneable接口。在这种情况下,Object.clone()会抛出CloneNotSupportedException

答案 2 :(得分:0)

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

更好的方法是通过Copy Constructor or Serialization来实现。这是my blog我已经详细讨论过它。希望它有所帮助:)

答案 3 :(得分:0)

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

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

  • 对可变列表的引用,虽然它允许自己进行变异,但它永远不会暴露给实际会使其变异的任何东西。如果其他代码不会改变列表或将其暴露给可能执行此操作的代码,则此引用可以与其他代码共享。包含此字段的对象的正确克隆可以包含对原始列表或包含相同项目的其他列表的引用。

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

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

  • 对此对象拥有的可变列表的引用,但其他对象也有一个引用,用于观察此对象的状态或向此对象提供信息。保存此字段的对象无法单独正确克隆,但可以克隆一组相互连接的对象,并为新的对象集提供一组互连,这些互连与原始组中的对象同构。

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