深度克隆实用程序推荐 [关闭]

Posted

技术标签:

【中文标题】深度克隆实用程序推荐 [关闭]【英文标题】:Deep clone utility recommendation [closed] 【发布时间】:2010-10-14 12:44:54 【问题描述】:

是否有任何用于深度克隆 java 集合的实用程序:

数组 列表 地图

注意:更喜欢一些不使用序列化的解决方案,但使用 Object.clone() 方法。我可以确定我的自定义对象将实现 clone() 方法,并且将仅使用可克隆的 java 标准类...

【问题讨论】:

How do you make a deep copy of an object in Java? 的可能重复项 使用克隆库为我节省了一天! github.com/kostaskougios/cloning 【参考方案1】:

我认为之前的绿色答案不好,你为什么会问?

它增加了很多代码 它要求您列出所有要复制的字段并执行此操作 当使用 clone() 时,这不适用于列表 (HashMap 的 clone() 是这样说的:返回这个 HashMap 实例的浅表副本:键和值本身没有被克隆。)所以你最终手动完成(这让我哭了)

哦,顺便说一句,序列化也很糟糕,你可能不得不到处添加 Serializable(这也让我哭了)。

那么解决办法是什么:

Java 深度克隆库 克隆库 是一个小型的开源(apache 许可证)java 库,它可以深度克隆对象。对象不必实现 Cloneable 接口。实际上,这个库可以克隆任何 java 对象。如果您不希望修改缓存的对象或想要创建对象的深层副本,则可以在缓存实现中使用它。

Cloner cloner=new Cloner();
XX clone = cloner.deepClone(someObjectOfTypeXX);

查看https://github.com/kostaskougios/cloning

【讨论】:

Cloner 是一个很棒的库(只是它的性能有时让我哭泣......但我想你不能用反射做得更好) 这个解决方案看起来更好。没试过,但是这个帖子里有几个 cmets 对 cloner 非常积极。 不适用于 android... 之前的绿色答案是什么? 这个库有一个大问题。如果你像这样使用 deppClone() ,就会发生过多的复制!!!我的意思是,如果你有一个“枚举”,它在它的主体中声明一个“抽象”方法,并在从该枚举克隆一个字段后,强制每个常量为该抽象方法提供不同的实现,“==”不再适用于那些常数!这很容易导致问题......库中有一些解决方案。 registerFastCloner() 和 registerImmutable() 都可以是一个解决方案,还没有尝试过......而且我不知道它是否可以解决!【参考方案2】:

在 Java 中复制对象的所有方法都有严重的缺陷:

克隆

    clone() 方法是受保护的,因此您不能直接调用它,除非相关类使用公共方法覆盖它。 clone() 不调用构造函数。任何构造函数。它将分配内存,分配内部class 字段(您可以通过getClass() 读取)并复制原始字段。

有关 clone() 的更多问题,请参阅 Joshua Bloch 的书“Effective Java, Second Edition”的第 11 项

序列化

序列化更糟糕;它有很多clone() 的缺陷,还有一些。 Joshua 有一个完整的章节,仅此主题就有四个项目。

我的解决方案

我的解决方案是为我的项目添加一个新界面:

public interface Copyable<T> 
    T copy ();
    T createForCopy ();
    void copyTo (T dest);

代码如下所示:

class Demo implements Copyable<Demo> 
    public Demo copy () 
        Demo copy = createForCopy ();
        copyTo (copy);
        return copy;
    
    public Demo createForCopy () 
        return new Demo ();
    
    public void copyTo (Demo dest)
        super.copyTo (dest);
        ...copy fields of Demo here...
    

不幸的是,我必须将此代码复制到我的所有对象中,但它始终是相同的代码,因此我可以使用 Eclipse 编辑器模板。优点:

    我可以决定调用哪个构造函数以及如何初始化哪个字段。 初始化以确定的顺序进行(根类到实例类) 我可以重用现有对象并覆盖它们 键入安全 单身人士保持单身

对于标准 Java 类型(如集合等),我使用一个可以复制这些的实用程序类。这些方法有标志和回调,所以我可以控制副本的深度。

【讨论】:

我通过在我需要克隆的所有类中实现 clone() 做了类似的事情。最大的问题是,如果我有集合,我必须遍历它并自己复制它...... 使用一个辅助函数,它接受一个集合并返回一个 ArrayList:因为你知道大小,所以它只会分配一次内存,而 ArrayList 对于通常的访问来说很快。 createForCopy 需要返回一个Demo 你会考虑展示 Eclipse 编辑器模板吗?【参考方案3】:

对集合进行浅层克隆很容易,但如果您想进行深度克隆,库可能比手动编码更好(因为您还想克隆集合内部的元素) .

就像this answer 一样,我使用了Cloner library 并专门针对XStream(可以通过序列化然后反序列化“克隆”)和二进制序列化对其进行了性能测试。虽然 XStream 在序列化到/从 xml 方面非常快,但 Cloner 在克隆方面要快得多:

0.0851 毫秒:xstream(通过序列化/反序列化克隆) 0.0223 ms:二进制序列化(通过序列化/反序列化克隆) 0.0017 毫秒:克隆器 * 克隆一个简单对象(两个字段)的平均时间,没有默认的公共构造函数。运行 10,000 次。

除了速度快之外,还有更多选择克隆机的理由:

    执行任何对象的深度克隆(即使是那些您不自己编写的对象) 您不必在每次添加字段时都更新您的 clone() 方法 您可以克隆没有默认公共构造函数的对象 与 Spring 配合使用 (优化)不会克隆已知的不可变对象(如整数、字符串等)

    易于使用。示例:

    cloner.deepClone(anyObject);

【讨论】:

* 克隆一个简单对象(两个字段)的平均时间,没有默认值——这看起来不像是“深度克隆”的正确测量。正确的测试将至少有两层嵌套集合。【参考方案4】:

我是 Brad 提出的克隆器库的创建者。这是一种克隆对象的解决方案,无需编写任何额外代码(无需可序列化对象或 impl clone() 方法)

正如 Brad 所说,它非常快,最近我上传了一个更快的版本。请注意,手动实现 clone() 方法会比克隆 lib 更快,但同样需要编写大量代码。

Cloner lib 对我来说工作得很好,因为我在一个流量非常大的网站(每天大约 100 万个请求)的缓存实现中使用它。每个请求缓存应该克隆大约 10 个对象。它非常可靠和稳定。但请注意,克隆并非没有风险。该库可以配置为打印它在开发期间克隆的每个类实例。通过这种方式,您可以检查它是否克隆了您认为应该克隆的内容 - 对象图可能非常深,并且可能包含对大量对象的引用。使用克隆库,您可以指示它不要克隆您不想要的对象,即单例。

【讨论】:

库看起来不错,过段时间会检查... "使用克隆库,您可以指示它不要克隆您不想要的对象" 我尝试这样做,但不能,我如何要求它不要克隆某些字段? 问候康斯坦丁诺斯。我正在使用您的库而不是 Apache utils 库(在使用 SerializableUtils 进行强制转换时遇到问题)。你的工作没有问题。干得好!。 我用你的lib很久了,真的很棒!谢谢你! D:【参考方案5】:

深度克隆任意集合的一种通用方法是将其序列化为流,然后将其读回到新集合中。您将重新水合完全没有的新对象与旧的任何关系,除了相同的副本。

查看 Bruno 的回答以获取指向 Apache Commons serialization utility classes 的链接,如果这是您决定采取的路线,这将非常有帮助。

【讨论】:

序列化解决方案很好,但我想到了没有它的东西。我可以保证我的自定义对象将使用 clone() 方法正确地进行深度克隆,但我需要帮助标准 java 类... 序列化克隆方式很好,但我有一些不受我控制且不可序列化的字段...【参考方案6】:

一种可能性是使用序列化

Apache Commons 提供SerializationUtils

【讨论】:

【参考方案7】:

我使用了这个cloning 库,发现它非常有用。 因为它有一些限制(我需要更细粒度的控制 在克隆过程中:哪个领域,在什么背景下以及多深 应该被克隆等),我已经创建了它的扩展版本。 您可以通过在其中添加注释来控制字段的克隆 实体类。

只是为了了解一下,这里是一个示例类:

public class CloneMePlease 
    @Clone(Skip.class)
    String id3 = UUID.randomUUID().toString();

    @Clone(Null.class)
    String id4 = UUID.randomUUID().toString();

    @Clone(value = RandomUUID.class, groups=CustomActivationGroup1.class)
    String id5 = UUID.randomUUID().toString();

    @Clone.List(
            @Clone(groups=CustomActivationGroup2.class, value=Skip.class),
            @Clone(groups=CustomActivationGroup3.class, value=Copy.class))
    Object activationGroupOrderTest = new Object();

    @Clone(LongIncrement.class)
    long version = 1l;

    @PostClone
    private void postClone(CloneMePlease original, @CloneInject CloneInjectedService service)
         //do stuff with the original source object in the context of the cloned object
         //you can inject whatewer service you want, from spring/guice to perform custom logic here
    

更多细节在这里: https://github.com/mnorbi/fluidity-cloning

还有一个特定于休眠的扩展,以备不时之需。

【讨论】:

【参考方案8】:

先使用序列化,然后再使用反序列化,但请注意,这种方法仅适用于没有瞬态字段的 Serializable 类。此外,您的单身人士将不再是单身人士。

【讨论】:

在运行时或在程序的不同运行之间使用序列化和反序列化来克隆内存对象是一个坏主意。有关为什么会这样的更多信息,请谷歌:“为什么序列化和反序列化不好”。

以上是关于深度克隆实用程序推荐 [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

推荐颜色选择器实用程序,支持Delphi颜色代码[关闭]

什么时候使用深度优先搜索 (DFS) 与广度优先搜索 (BFS) 比较实用? [关闭]

MacOsx实用软件推荐(Java程序员版)

实用程序类是邪恶的吗? [关闭]

第 017 期 8 个实用的 Lodash 方法介绍

是否有任何实用程序可以帮助我重构 CSS [关闭]