Reputation: 5557
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.
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)
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 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
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.
Upvotes: 200
Views: 168043
Reputation: 21
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.
Upvotes: 1
Reputation: 146
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
Upvotes: 0
Reputation: 408
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);
Upvotes: 1
Reputation: 8955
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);
}
Upvotes: 4
Reputation: 597076
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-beanutils BeanUtils - in most cases.
Spring BeanUtils - if you are already using spring and hence have this utility on the classpath.
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.
Upvotes: 175
Reputation: 51
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));
Upvotes: 5
Reputation: 16486
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.
Upvotes: 10
Reputation: 3649
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);)
Upvotes: 39
Reputation: 28686
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
Upvotes: 2
Reputation: 14077
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.
Upvotes: 1
Reputation: 6466
I'd recommend the DIY way which, combined with a good hashCode() and equals() method should be easy to proof in a unit test.
Upvotes: 1