对于Java集合,是否存在进行深度克隆的实用工具:
- 数组
- 列表
- 映射
注意:最好使用Object.clone()方法而不是序列化来解决问题。我可以确保我的自定义对象将实现clone()方法并且仅使用可克隆的Java标准类...
我认为之前的绿色答案很不好,你可能会问为什么?
哦,顺便说一句,序列化也很糟糕,你可能需要在很多地方添加Serializable(这也让我哭了)。
那么解决方案是什么呢:
Java Deep-Cloning库 该克隆库是一个小型的、开源的(apache许可证)Java库,可以深度克隆对象。对象不必实现Cloneable接口。实际上,该库可以克隆任何Java对象。如果您不希望缓存对象被修改或者想要创建对象的深层副本,则可以将其用于缓存实现等情况。
Cloner cloner=new Cloner();
XX clone = cloner.deepClone(someObjectOfTypeXX);
所有Java中复制对象的方法都有严重的缺陷:
Clone
class
字段(可以通过getClass()
读取),并复制原始字段。有关clone()更多问题,请参见Joshua Bloch的书 "Effective Java, Second Edition" 的第11项。
Serialize
Serialize甚至更糟糕;它具有许多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类型(如集合等),我使用一个实用程序类来复制它们。这些方法具有标志和回调,因此我可以控制要复制多深。
浅层克隆集合很容易,但如果你想要进行深层克隆,使用库可能比手动编码更好(因为你想要克隆集合中的元素)。
就像这个答案一样,我使用了Cloner库,并在性能测试中与XStream(可以通过序列化然后反序列化来“克隆”)和二进制序列化进行了比较。尽管XStream在序列化/反序列化方面非常快速,但是Cloner在克隆方面要快得多:
0.0851毫秒:xstream(通过序列化/反序列化克隆)
0.0223毫秒:二进制序列化(通过序列化/反序列化克隆)
0.0017毫秒:cloner
* 平均时间克隆一个简单对象(两个字段),不使用默认公共构造函数。运行10,000次。
除了速度快之外,选择cloner的其他原因如下:
易于使用。示例:
cloner.deepClone(anyObject);
我是 cloner lib 的创建者,就是 Brad 展示的那个。这是一个解决方案,可以在不需要编写额外代码(无需可序列化的对象或实现 clone() 方法)的情况下克隆对象。
正如 Brad 所说的那样,它非常快,并且最近我上传了一个更快的版本。请注意,手动实现 clone() 方法比使用 clone lib 更快,但是你需要编写大量的代码。
Cloner lib 在我的缓存实现中工作得非常好,该站点有非常重的流量(~1 百万次请求/天)。每个请求应该克隆约 10 个对象。它非常可靠和稳定。但请注意,克隆并不是没有风险的。该库可以配置为在开发过程中 println 克隆的每个类实例。这样,您可以检查它是否克隆了您认为应该克隆的内容 - 对象图可以非常深,并且可能包含对大量对象的意外引用。使用 clone lib,您可以指示它不要克隆您不想要的对象,例如单例。
深度克隆任意集合的一种通用方式是将其序列化到流中,然后读取到一个新集合中。你将重新生成全新的对象,它们与旧对象没有任何关系,除了是完全相同的副本。
如果你决定采取这种方式,请查看Bruno的答案,其中包含Apache Commons序列化工具类的链接,这些工具类将非常有用。
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
}
}
使用序列化和反序列化,但请注意,此方法仅适用于没有瞬态字段的可序列化类。此外,您的单例将不再是单例。