是否有用于深度克隆Java集合的实用程序:
- 数组
- 清单
- 地图
注意:更喜欢一些不使用序列化但使用Object.clone()方法的解决方案。我可以确定我的自定义对象将实现clone()方法,并且仅使用可克隆的Java标准类...
是否有用于深度克隆Java集合的实用程序:
注意:更喜欢一些不使用序列化但使用Object.clone()方法的解决方案。我可以确定我的自定义对象将实现clone()方法,并且仅使用可克隆的Java标准类...
Answers:
我认为以前的绿色答案很不好,您为什么会问?
哦,顺便说一句,序列化也很糟糕,您可能必须在各处添加Serializable(这也让我哭了)。
那么解决方案是什么:
Java深度 克隆库克隆库是一个小型的,开源的(Apache许可证)Java库,用于深度克隆对象。对象不必实现Cloneable接口。实际上,该库可以克隆任何java对象。如果您不希望修改缓存的对象,或者想要创建对象的深层副本,则可以在缓存实现中使用它。
Cloner cloner=new Cloner();
XX clone = cloner.deepClone(someObjectOfTypeXX);
用Java复制对象的所有方法都有严重的缺陷:
克隆
class
字段(您可以通过读取getClass()
)并复制原始字段。有关clone()的更多问题,请参见约书亚·布洛赫(Joshua Bloch)的书“有效的Java,第二版”的第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类型(例如集合等),我使用可以复制它们的实用程序类。这些方法具有标志和回调,因此我可以控制副本的深度。
浅克隆的集合是很容易的,但如果你想深克隆,因为要克隆的元素库可能会做你比手更好的编码它(里面集合为好)。
就像这个答案一样,我使用了Cloner库,并针对XStream(可以先序列化然后反序列化“克隆”)和二进制序列化对它进行性能测试。尽管XStream可以非常快速地从xml序列化到/从xml序列化,但是Cloner的克隆速度要快得多:
0.0851 ms:xstream(通过序列化/反
序列化克隆)0.0223 ms:二进制序列化(通过序列化/反序列化克隆)
0.0017 ms:cloneer
*克隆简单对象(两个字段)的平均时间,没有默认的公共构造函数。运行10,000次。
除了快速之外,还有更多选择克隆器的原因:
易于使用。例:
cloner.deepClone(anyObject);
我是布拉德介绍的克隆库lib的创建者。这是一种无需编写任何额外代码即可克隆对象的解决方案(无需可序列化的对象或impl clone()方法)
正如Brad所说的那样,速度非常快,最近我上传了一个更快的版本。请注意,手动实现clone()方法将比克隆lib更快,但是再次,您将需要编写大量代码。
Cloner lib对我来说效果很好,因为我在流量非常大(每天约100万个请求)的站点的缓存实现中使用它。缓存应为每个请求克隆大约10个对象。这是相当可靠和稳定的。但是请注意,克隆并非没有风险。可以将lib配置为println在开发期间克隆的每个类实例。这样,您可以检查它是否克隆了您认为应该克隆的对象-对象图可能非常深,并且可以包含对大量对象的引用。使用clone lib,您可以指示它不要克隆不需要的对象,即单例。
深度克隆任意集合的一种通用方法是将其序列化为流,然后将其读回到新的集合中。您将为与旧对象没有任何关系的全新对象重新补水,除了它们是相同的副本。
请查看Bruno的答案,以获取指向Apache Commons序列化实用程序类的链接,如果这是您决定采用的方法,这将非常有用。
一种可能是使用序列化:
Apache Commons提供SerializationUtils
我已经使用了此克隆库,发现它非常有用。由于它有一些局限性(我需要对克隆过程进行更细粒度的控制:应该克隆哪个字段,在什么上下文中以及要克隆的深度等),所以我创建了它的扩展版本。您可以通过在实体类中注释字段来控制字段的克隆。
只是为了尝一尝,下面是一个示例类:
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
还有一个休眠特定的扩展,以备不时之需。