Java:深度克隆/复制实例的推荐解决方案

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2156120/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 04:12:47  来源:igfitidea点击:

Java: recommended solution for deep cloning/copying an instance

javaclone

提问by Guillaume

I'm wondering if there is a recommended way of doing deep clone/copy of instance in java.

我想知道是否有推荐的方法在 java 中进行实例的深度克隆/复制。

I have 3 solutions in mind, but I can have miss some, and I'd like to have your opinion

我有 3 个解决方案,但我可能会错过一些,我想听听您的意见

edit: include Bohzo propositon and refine question: it's more about deep cloning than shallow cloning.

编辑:包括 Bohzo 命题并提炼问题:它更多地是关于深克隆而不是浅克隆。

Do it yourself:

自己做:

code the clone by hand properties after properties and check that mutable instances are cloned too.
pro:
- control of what will be performed
- quick execution
cons:
- tedious to write and maintain
- bug prone (copy/paste failure, missing property, reassigned mutable property)

在属性之后手动对克隆进行编码,并检查是否也克隆了可变实例。
优点:
- 控制将要执行的内容
- 快速执行
缺点:
- 编写和维护繁琐
- 容易出错(复制/粘贴失败,缺少属性,重新分配可变属性)

Use reflection:

使用反射:

With your own reflection tools or with an external helper (like jakarta common-beans) it is easy to write a generic copy method that will do the job in one line.
pro:
- easy to write
- no maintenance
cons:
- less control of what happens
- bug prone with mutable object if the reflection tool does not clone sub objects too
- slower execution

使用您自己的反射工具或外部帮助程序(如 jakarta common-beans),很容易编写一个通用的复制方法,该方法将在一行中完成工作。
亲:
-容易编写
-无需维护
缺点:
-发生了什么较少的控制
-错误倾向的with可变对象,如果反射工具不克隆子对象太
-慢执行

Use clone framework:

使用克隆框架:

Use a framework that do it for you, like :
commons-lang SerializationUtils
Java Deep Cloning Library
Dozer
Kryo

使用适合您的框架,例如:
commons-lang SerializationUtils
Java Deep Cloning Library
Dozer
Kryo

pro:
- same as reflection
- more control over what will be exactly be cloned.
cons:
- every mutable instance is fully cloned, even at the end of the hierarchy
- could be very slow to execute

优点:
- 与反射相同
- 更好地控制将被克隆的内容。
缺点:
- 每个可变实例都被完全克隆,即使在层次结构的末尾
- 执行起来可能很慢

Use bytecode instrumentation to write clone at runtime

使用字节码检测在运行时编写克隆

javassit, BCELor cglibmight be use to generate a dedicated cloner as fast as one hand writed. Someone knows a lib using one of these tools for this purpose ?

javassitBCELcglib可用于生成一个专用的克隆器,就像一个人写的一样快。有人知道为此目的使用这些工具之一的库吗?

What I have missed here ?
Which one would you recommend ?

我在这里错过了什么?
你会推荐哪一个?

Thanks.

谢谢。

采纳答案by Bozho

For deep cloning (clones the entire object hierarchy):

对于深度克隆(克隆整个对象层次结构):

  • commons-lang SerializationUtils- using serialization - if all classes are in your control and you can force implementing Serializable.

  • Java Deep Cloning Library- using reflection - in cases when the classes or the objects you want to clone are out of your control (a 3rd party library) and you can't make them implement Serializable, or in cases you don't want to implement Serializable.

  • commons-lang SerializationUtils- 使用序列化 - 如果所有类都在您的控制之下并且您可以强制实现Serializable.

  • Java Deep Cloning Library- 使用反射 - 在您想要克隆的类或对象超出您的控制(第 3 方库)并且您无法实现它们的情况下Serializable,或者在您不想实现的情况下Serializable.

For shallow cloning (clones only the first level properties):

对于浅克隆(仅克隆第一级属性):

I deliberately omitted the "do-it-yourself" option - the API's above provide a good control over what to and what not to clone (for example using transient, or String[] ignoreProperties), so reinventing the wheel isn't preferred.

我故意省略了“自己动手”选项 - 上面的 API 可以很好地控制克隆什么和不克隆什么(例如使用transient, 或String[] ignoreProperties),因此重新发明轮子不是首选。

回答by Dominik Sandjaja

I'd recommend the DIY way which, combined with a good hashCode() and equals() method should be easy to proof in a unit test.

我推荐 DIY 方式,结合一个好的 hashCode() 和 equals() 方法应该很容易在单元测试中证明。

回答by x4u

I'd suggest to override Object.clone(), call super.clone() first and than call ref = ref.clone() on all references that you want to have deep copied. It's more or less Do it yourselfapproach but needs a bit less coding.

我建议覆盖 Object.clone(),首先调用 super.clone(),然后在所有要深度复制的引用上调用 ref = ref.clone()。它或多或少是自己动手的方法,但需要更少的编码。

回答by Yoni Roit

Depends.

要看。

For speed, use DIY. For bulletproof, use reflection.

为了速度,请使用 DIY。为了防弹,请使用反射。

BTW, serialization is not the same as refl, as some objects may provide overridden serialization methods (readObject/writeObject) and they can be buggy

顺便说一句,序列化与 refl 不同,因为某些对象可能提供覆盖的序列化方法(readObject/writeObject),并且它们可能有问题

回答by LeWoody

Joshua Bloch's book has a whole chapter entitled "Item 10: Override Clone Judiciously"in which he goes into why overriding clone for the most part is a bad idea because the Java spec for it creates many problems.

Joshua Bloch 的书有一整章题为“Item 10: Override Clone Judiciously”,他在其中讨论了为什么在大多数情况下覆盖克隆是个坏主意,因为 Java 规范会产生很多问题。

He provides a few alternatives:

他提供了一些替代方案:

  • Use a factory pattern in place of a constructor:

         public static Yum newInstance(Yum yum);
    
  • Use a copy constructor:

         public Yum(Yum yum);
    
  • 使用工厂模式代替构造函数:

         public static Yum newInstance(Yum yum);
    
  • 使用复制构造函数:

         public Yum(Yum yum);
    

All of the collection classes in Java support the copy constructor (e.g. new ArrayList(l);)

Java 中的所有集合类都支持复制构造函数(例如 new ArrayList(l);)

回答by Andrey Chaschev

Since version 2.07 Kryo supports shallow/deep cloning:

从 2.07 版开始,Kryo 支持浅/深克隆

Kryo kryo = new Kryo();
SomeClass someObject = ...
SomeClass copy1 = kryo.copy(someObject);
SomeClass copy2 = kryo.copyShallow(someObject);

Kryo is fast, at the and of their page you may find a list of companies which use it in production.

Kryo 很快,在他们的页面上,您可以找到在生产中使用它的公司列表。

回答by Ranx

Use XStream toXML/fromXML in memory. Extremely fast and has been around for a long time and is going strong. Objects don't need to be Serializable and you don't have use reflection (although XStream does). XStream can discern variables that point to the same object and not accidentally make two full copies of the instance. A lot of details like that have been hammered out over the years. I've used it for a number of years and it is a go to. It's about as easy to use as you can imagine.

在内存中使用 XStream toXML/fromXML。速度极快,已经存在了很长时间,并且发展势头强劲。对象不需要是可序列化的,也不需要使用反射(尽管 XStream 可以)。XStream 可以识别指向同一个对象的变量,并且不会意外地制作实例的两个完整副本。多年来,已经敲定了很多这样的细节。我已经使用它很多年了,这是一个去。它与您想象的一样容易使用。

new XStream().toXML(myObj)

or

或者

new XStream().fromXML(myXML)

To clone,

要克隆,

new XStream().fromXML(new XStream().toXML(myObj))

More succinctly:

更简洁:

XStream x = new XStream();
Object myClone = x.fromXML(x.toXML(myObj));

回答by tiboo

For complicated objects and when performance is not significant i use gsonto serialize the object to json text, then deserialize the text to get new object.

对于复杂的对象,当性能不重要时,我使用gson将对象序列化为 json 文本,然后反序列化文本以获取新对象。

gson which based on reflection will works in most cases, except that transientfields will not be copied and objects with circular reference with cause StackOverflowError.

基于反射的 gson 将在大多数情况下工作,除了transient不会复制字段和具有原因的循环引用对象StackOverflowError

public static <ObjectType> ObjectType Copy(ObjectType AnObject, Class<ObjectType> ClassInfo)
{
    Gson gson = new GsonBuilder().create();
    String text = gson.toJson(AnObject);
    ObjectType newObject = gson.fromJson(text, ClassInfo);
    return newObject;
}
public static void main(String[] args)
{
    MyObject anObject ...
    MyObject copyObject = Copy(o, MyObject.class);

}

回答by Alexander Maslew

For deep cloning implement Serializable on every class you want to clone like this

对于深度克隆,在你想要克隆的每个类上实现 Serializable

public static class Obj implements Serializable {
    public int a, b;
    public Obj(int a, int b) {
        this.a = a;
        this.b = b;
    }
}

And then use this function:

然后使用这个函数:

public static Object deepClone(Object object) {
    try {
        ByteArrayOutputStream baOs = new ByteArrayOutputStream();
        ObjectOutputStream oOs = new ObjectOutputStream(baOs);
        oOs.writeObject(object);
        ByteArrayInputStream baIs = new ByteArrayInputStream(baOs.toByteArray());
        ObjectInputStream oIs = new ObjectInputStream(baIs);
        return oIs.readObject();
    }
    catch (Exception e) {
        e.printStackTrace();
        return null;
    }
}

like this: Obj newObject = (Obj)deepClone(oldObject);

像这样: Obj newObject = (Obj)deepClone(oldObject);