深度克隆实用程序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);
所有用Java复制对象的方法都有严重的缺陷:
克隆
- clone()方法是受保护的,所以你不能直接调用它,除非有问题的类用public方法覆盖它。
- 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编辑器模板。 优点:
- 我可以决定调用哪个构造函数,以及如何初始化哪个字段。
- 初始化以确定的顺序发生(根类到实例类)
- 我可以重用现有的对象并覆盖它们
- types安全
- 单身人士保持单身
对于标准的Javatypes(比如集合等),我使用一个可以复制这些类的实用类。 这些方法有标志和callback,所以我可以控制副本的深度。
浅克隆一个集合是很容易的,但是如果你想深入克隆,一个库可能会比手工编码更好(因为你也想克隆集合中的元素)。
就像这个答案一样 ,我已经使用了Cloner库,并且特别针对XStream(可以通过序列化然后反序列化克隆)和二进制序列化来进行性能testing。 尽pipeXStream在xml序列化方面速度非常快,但是Cloner的克隆速度要快得多:
0.0851毫秒:xstream(通过序列化/反序列化克隆)
0.0223毫秒:二进制序列化(克隆序列化/反序列化)
0.0017毫秒:克隆
* 平均时间来克隆一个简单的对象(两个字段),没有默认的公共构造函数。 运行10000次。
除了快速,这里有更多的理由select克隆:
- 执行任何对象的深层克隆(即使是你自己不写的对象)
- 每次添加字段时,不必保持clone()方法是最新的
- 您可以克隆没有默认公共构造函数的对象
- 与spring一起工作
- (优化)不会克隆已知的不可变对象(如Integer,String等)
-
使用方便。 例:
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会话中深入克隆脱离实体。
使用序列化,然后反序列化,但请注意,这种方法只适用于没有瞬态字段的可序列化类。 此外,你的单身人士将不再是单身人士。