深度克隆实用程序build议

是否有任何实用程序深入克隆Java集合:

  • 数组
  • 清单
  • 地图

注意:不要使用序列化,而要使用Object.clone()方法。 我可以肯定,我的自定义对象将实现clone()方法,并将只使用可克隆的java标准类…

我认为以前的绿色答案是不好的 ,你为什么要问?

  • 它增加了很多代码
  • 它要求您列出所有要复制的字段并执行此操作
  • 当使用clone()时,这对列表不起作用(这是HashMap的clone()所说的:返回这个HashMap实例的一个浅拷贝:key和value不会被克隆。我哭了)

哦,顺便说一句,序列化也不好,你可能不得不在所有的地方添加Serializable(这也让我哭了)。

那么解决scheme是什么?

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

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

通过https://github.com/kostaskougios/cloning查看;

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

克隆

  1. clone()方法是受保护的,所以你不能直接调用它,除非有问题的类用public方法覆盖它。
  2. clone()不调用构造函数。 任何构造函数。 它将分配内存,分配内部class字段(可以通过getClass()读取)并复制原始字段。

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

连载

序列化更糟糕; 它有许多clone()的缺陷,然后一些。 约书亚整个篇章仅涉及这个主题的四个项目。

我的解决scheme

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

 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编辑器模板。 优点:

  1. 我可以决定调用哪个构造函数,以及如何初始化哪个字段。
  2. 初始化以确定的顺序发生(根类到实例类)
  3. 我可以重用现有的对象并覆盖它们
  4. types安全
  5. 单身人士保持单身

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

浅克隆一个集合是很容易的,但是如果你想深入克隆,一个库可能会比手工编码更好(因为你也想克隆集合中的元素)。

就像这个答案一样 ,我已经使用了Cloner库,并且特别针对XStream(可以通过序列化然后反序列化克隆)和二进制序列化来进行性能testing。 尽pipeXStream在xml序列化方面速度非常快,但是Cloner的克隆速度要快得多:

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

除了快速,这里有更多的理由select克隆:

  1. 执行任何对象的深层克隆(即使是你自己不写的对象)
  2. 每次添加字段时,不必保持clone()方法是最新的
  3. 您可以克隆没有默认公共构造函数的对象
  4. 与spring一起工作
  5. (优化)不会克隆已知的不可变对象(如Integer,String等)
  6. 使用方便。 例:

    cloner.deepClone(anyObject);

我是布莱克所提出的克隆人库的创造者。 这是克隆对象的解决scheme,无需编写任何额外的代码(不需要可序列化对象或impl clone()方法)

Brad说,速度非常快,最近我上传了一个更快的版本。 请注意,手动实现clone()方法将比克隆库更快,但是再次需要编写大量的代码。

Cloner lib对我来说工作得非常好,因为我在一个stream量非常大的站点(每天约100万次请求)的caching实现中使用它。 caching应该每个请求克隆大约10个对象。 这是相当可靠和稳定。 但请注意,克隆不是没有风险的。 可以将libconfiguration为在开发过程中打印它克隆的每个类实例。 这样你可以检查它是否克隆你认为应该克隆的东西 – 对象图可以是非常深的,并且可以包含对数量惊人的对象的引用。 使用克隆库,你可以指示它不克隆你不想要的对象,即单例。

深度克隆任意集合的一种常用方法是将其序列化为stream,然后将其读回到新的集合中。 您将完全补充与新旧对象没有任何关系的新对象,而不是相同的副本。

查看Bruno的答案 ,链接到Apache Commons序列化实用程序类 ,如果这是您决定采用的路线,这将非常有帮助。

一种可能是使用序列化

Apache Commons提供了SerializationUtils

我已经使用这个克隆库,发现它非常有用。 由于它有一些限制(我需要对克隆过程进行更细致的控制:哪一个领域,什么上下文以及如何深度克隆等),我已经创build了一个扩展版本。 您可以通过在实体类中对其进行注释来控制字段的复制。

为了得到它的味道,这里是一个例子类:

 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

如果需要的话,还有一个hibernate特定的扩展。

本文可能会解决您的问题: http : //www.matthicks.com/2008/05/fastest-deep-cloning.html

他们使用序列化和reflection。

这是一个很好的图书馆,可以从hibernate会话中深入克隆脱离实体。

http://sourceforge.net/projects/cloneutils/

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