Java: recommended solution for deep cloning/copying an instance
Asked Answered
D

11

196

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

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

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

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

Use clone framework:

Use a framework that do it for you, like :
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, BCEL or cglib might 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 ?

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

Thanks.

Dryfoos answered 28/1, 2010 at 16:34 Comment(1)
apparently Java Deep Cloning Library moved here : code.google.com/p/cloningCorell
N
170

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.

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.

Neufer answered 28/1, 2010 at 17:4 Comment(9)
Thanks Bozho, that's valuable. And I agree with you about the DIY option ! Have you ever tried the commons serialzation and/or the deep cloning lib ? What about the perfs ?Dryfoos
yes, I have used all of the above options, for the above reasons :) only the cloning library had some issues when CGLIB proxies were involved, and missed some desired functionality, but I think that should be fixed now.Neufer
Hey, if my Entity is attached and I have lazy things, does SerializationUtils checks the database for the lazy properties ? Cuz this is what I want, and it doesn't !Verina
if you have an active session - yes, it does.Neufer
@Neufer So you mean if all the objects within the bean are implementing serializable, org.apache.commons.beanutils.BeanUtils.cloneBean(obj) will do a deep copy ?Denudation
apparently Java Deep Cloning Library moved here : code.google.com/p/cloningCorell
Good tips...I also use the cloning-by-serialization concept to clone objects via JAXB (marshal the object to a byte array, then unmarshal a new instance of the same object);Rubi
@Bozho: It seems there are two cons in two frameworks, (1) Spring BeanUtils => does a shallow copy only but developers can customize to ignore some properties (2) Apache BeanUtils => may do a deep copy (as you said) but no provisions for customization to skip few fields while coping (like ignoreProperties in Spring BeanUtils)....any suggestion?Rampant
Java Deep Cloning Library is not working with me. Giving me ClassNotFound exceptionKigali
K
38

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.

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);
    

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

Karafuto answered 28/1, 2010 at 22:29 Comment(5)
Agreed. In my project I defined a Copyable interface that contains a getCopy() method. Just use the prototype pattern manually.Gass
Well I was not asking about the cloneable interface, but how to perform a deep clone/copy operation. With a constructor or a factory you still need to create your new instance from your source.Dryfoos
@Dryfoos I think you need to be careful in using the words deep clone/copy. Clone and copy in java do NOT mean the same thing. The Java spec has more to say about this.... I think you want a deep copy from what I can tell.Karafuto
OK Java spec is accurate about what a clone is... But we can also speak of the clone in a more common meaning... For example, one of the lib recommended by bohzo is named 'Java Deep Cloning Library'...Dryfoos
@LWoodyiii this newInstance() method and the Yum constructor would do deep copy or shallow copy?Estes
P
10

Since version 2.07 Kryo supports shallow/deep cloning:

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.

Professionalism answered 22/11, 2013 at 10:29 Comment(2)
how does kryo do clone without serialization? does it use reflection?Robby
Take care: if the classes not have no-arg constructor, Kryo will bot able to copyRoss
A
5

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.

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));
Alvera answered 28/7, 2015 at 2:46 Comment(1)
XStream xml is not extremely fast. No xml is extremely fast, and if you want to do deep copying quickly you certainly don't involve a parser. I seem to remember XStream has a an option to generate a binary representation though, which would be much more sensible.Bluegrass
A
4

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

gson which based on reflection will works in most cases, except that transient fields will not be copied and objects with circular reference with cause 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);

}
Androgen answered 29/11, 2016 at 7:59 Comment(0)
H
2

Depends.

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

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

Hereford answered 28/1, 2010 at 16:51 Comment(1)
reflection is not bullet proof: it can lead in some situation where your cloned object has reference to your source... If the source change, the clone will change too !Dryfoos
P
1

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

Photoplay answered 28/1, 2010 at 16:47 Comment(2)
well, the lazy me rants a lot when creating such a dummy code. But it is looking like the wiser path...Dryfoos
sorry, but DIY is the way to go ONLY if no other solution is appropriate for you..which is almost neverNeufer
P
1

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 yourself approach but needs a bit less coding.

Performing answered 28/1, 2010 at 16:48 Comment(1)
That's one of the many problems of the (broken) clone method: In a class hierarchy you always have to call super.clone(), which can easily be forgotten, that's why I would prefer using a copy constructor.Midships
L
1

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

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);

Logorrhea answered 1/5, 2020 at 15:58 Comment(0)
B
1

Take a look to sugar-cubes-cloner library. It is very fast and well documented. It also allows the developer to configure the cloning process very flexible on the level of classes, fields and objects.

Braga answered 28/8, 2023 at 20:45 Comment(0)
A
0

Just use MicroStream Object Copier.

ObjectCopier objectCopier = ObjectCopier.New();
Customer customer = root.getCustomer(id);
Customer customerCopy = objectCopier.copy(customer);

This utility provides the full deep copy of any object graph in Java. Be careful of the cyclic references. You can easily make o copy of your whole memory graph.

https://docs.microstream.one/manual/storage/storing-data/deep-copy.html

Ait answered 28/7, 2022 at 12:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.