Deep clone utility recommendation [closed]
Asked Answered
R

8

78

Is there any utility for deep cloning for java collections:

  • Arrays
  • Lists
  • Maps

NOTE: prefer some solution without usage of serialization, but with use of Object.clone() method. I can be sure that my custom object will implement clone() method and will use only java-standard classes that are cloneable...

Reger answered 20/3, 2009 at 12:3 Comment(2)
possible duplicate of How do you make a deep copy of an object in Java?Leotaleotard
Using cloning library saved the day for me! github.com/kostaskougios/cloningChangeover
Y
65

I think the previous green answer was bad , why you might ask?

  • It adds a lot of code
  • It requires you to list all fields to be copied and do this
  • This will not work for Lists when using clone() (This is what clone() for HashMap says: Returns a shallow copy of this HashMap instance: the keys and valuesthemselves are not cloned.) so you end up doing it manually (this makes me cry)

Oh and by the way serialization is also bad, you might have to add Serializable all over the place (this also makes me cry).

So what is the solution:

Java Deep-Cloning library The cloning library is a small, open source (apache licence) java library which deep-clones objects. The objects don't have to implement the Cloneable interface. Effectivelly, this library can clone ANY java objects. It can be used i.e. in cache implementations if you don't want the cached object to be modified or whenever you want to create a deep copy of objects.

Cloner cloner=new Cloner();
XX clone = cloner.deepClone(someObjectOfTypeXX);

Check it out at https://github.com/kostaskougios/cloning

Yesteryear answered 6/8, 2009 at 20:7 Comment(10)
Cloner is a great library (only its performance sometimes makes me cry... but I guess you can't do much better with reflection)Doyenne
This solution looks better. Haven't try it, but there is couple of comments in this thread that are very positive about cloner.Reger
Doesn't work on Android...Statics
what was the previous green answer?Endogamy
@Endogamy https://mcmap.net/q/93603/-deep-clone-utility-recommendation-closedYesteryear
There is one big problem with this library. If you use deppClone() just like that, too much copying occurs!!! What I mean by that is, if you have an "enum" that declares an "abstract" method in it's body and forces each constant to give a different implementation for that abstract method, after cloning a field from that enum, "==" no longer works for those constants!! Which can easily cause problems... There are some solutions in the library. registerFastCloner() and registerImmutable() could both be a solution, haven't tried them yet... And I don't know if it can be solved at all!Transitory
Look at this: package mx.tests; import com.rits.cloning.Cloner; public class ClonerTest { enum MyEnum { A { int getNumber() { return 11; } }, B { int getNumber() { return 22; } }; abstract int getNumber(); } static class Test { MyEnum m; public Test() { m = MyEnum.A; } } public static void main(final String[] args) { Test t1 = new Test(); Test t2 = new Test(); Cloner c = new Cloner(); t2 = c.deepClone(t2); System.out.println(t1.m == t2.m); } }Transitory
This library is not Java 10 compatible - do you know any alternative?Salpiglossis
Does this work on Android ? How to import this lib in my project ?Pinnatifid
Cloner isn't actively maintained anymore according to the current maintainer github.com/kostaskougios/cloning/issues/…Unhandy
E
20

All approaches to copy objects in Java have serious flaws:

Clone

  1. The clone() method is protected, so you can't call it directly unless the class in question overrides it with a public method.
  2. clone() doesn't call the constructor. Any constructor. It will allocate memory, assign the internal class field (which you can read via getClass()) and copy the fields of the original.

For more issues with clone(), see item 11 of Joshua Bloch's book "Effective Java, Second Edition"

Serialize

Serialize is even worse; it has many of the flaws of clone() and then some. Joshua has a whole chapter with four items for this topic alone.

My Solution

My solution is add a new interface to my projects:

public interface Copyable<T> {
    T copy ();
    T createForCopy ();
    void copyTo (T dest);
}

The code looks like this:

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...
    }
}

Unfortunately, I have to copy this code to all my objects but it's always the same code, so I can use an Eclipse editor template. Advantages:

  1. I can decide which constructor to call and how to initialize which field.
  2. Initialization happens in a deterministic order (root class to instance class)
  3. I can reuse existing objects and overwrite them
  4. Type safe
  5. Singletons stay singletons

For standard Java types (like collections, etc), I use a utility class which can copy those. The methods have flags and callbacks, so I can control how deep a copy should be.

Epanorthosis answered 20/3, 2009 at 13:52 Comment(4)
I did something similar by implementing clone() in all classes that I need to clone. Biggest problem is that if I have collection I have to iterate over it and copy it by my self...Reger
Use a helper function which accepts a Collection and returns an ArrayList: Since you know the size, that will allocate memory just once and ArrayList is fast for the usual kinds of access.Epanorthosis
createForCopy needs to return a DemoIrradiant
Would you consider showing the Eclipse editor template?Phyl
P
17

Shallow cloning a collection is easy, but if you want to deep clone, a library will probably do you better than hand coding it (since you want to clone the elements inside the collection as well).

Just like this answer, I've used the Cloner library and specifically performance tested it against XStream (which can 'clone' by serializing then deserializing) and binary serialization. Though XStream is very fast at serializing to/from xml, Cloner is much faster at cloning:

0.0851 ms : xstream (clone by serializing/deserializing)
0.0223 ms : binary serialization (clone by serializing/deserializing)
0.0017 ms : cloner
* average time to clone a simple object (two fields) and no default, public constructor. Run 10,000 times.

In addition to being fast, here are more reasons to choose cloner:

  1. performs a deep clone of any object (even those you don't write yourself)
  2. you don't have to keep your clone() method up-to-date each time you add a field
  3. you can clone objects that don't have a default public constructor
  4. works with Spring
  5. (optimization) doesn't clone known immutable objects (like Integer, String, etc.)
  6. easy to use. Example:

    cloner.deepClone(anyObject);

Parquetry answered 6/8, 2009 at 20:59 Comment(2)
* average time to clone a simple object (two fields) and no default – This doesn't look like proper measurement for "deep cloning". Proper test will have at least two levels of nested collections.Rondelet
Cloner isn't actively maintained anymore according to the current maintainer github.com/kostaskougios/cloning/issues/…Unhandy
P
17

I am the creator of the cloner lib, the one that Brad presented. This is a solution for cloning objects without having to write any extra code (no need for serializable objects or impl clone() method)

It's quite fast as Brad said, and recently I uploaded a version which is even faster. Note that manually implementing a clone() method will be faster than clone lib, but then again you'll need to write a lot of code.

Cloner lib has worked quite well for me since I am using it in a cache implementation for a site with very heavy traffic (~1 million requests/day). The cache should clone approximately 10 objects per request. It is quite reliable and stable. But please be aware that cloning is not without risk. The lib can be configured to println every class instance that it clones during dev. This way you can check if it clones what you think it should clone - object graphs can be very deep and can contain references to a surprisingly large amount of objects. With clone lib, you can instruct it to not clone the objects that you don't want, i.e. singletons.

Prurient answered 27/10, 2009 at 22:48 Comment(4)
The library looks fine, will check it some time later...Reger
"With clone lib, you can instruct it to not clone the objects that you don't want" I tried doing this, but could not, how do i ask it not to clone certain fields?Detrude
Greetings Konstantinos. I am using your library instead of the Apache utils one (Was having issues with casts with SerializableUtils). Yours is working without issues. Great work!.Fellows
I've use ur lib for a long time , it's really great ! tks ur ! D:Around
L
11

One general way to deep-clone an arbitrary collection is to serialize it to a stream, then read it back into a new collection. You'll be rehydrating completely new objects that don't have any relationship to the old ones, other than being identical copies.

Check out Bruno's answer for a link to the Apache Commons serialization utility classes, which will be very helpful if this is the route you decide to take.

Loudspeaker answered 20/3, 2009 at 12:9 Comment(3)
Serialization solution is fine, but I thought about something without it. I can garantie that my custom object will be correctly deep-cloned with clone() method, but I want helper that will do it for standard java classes...Reger
Serialization way to clone is fine, but I had some fields that are not under my control and that are not serializable...Reger
This is especially useful for testing scenarios, where performance is not as crucial, and the copy need not be functionally perfect.Resplendent
L
5

One possibility is to use serialization:

Apache Commons provides SerializationUtils

Loree answered 20/3, 2009 at 12:10 Comment(0)
E
2

I've used this cloning library and found it quite useful. Since it had a few limitations (I needed finer grained control over the cloning process: which field, in what context and how deeply should be cloned etc.), I've created an extended version of it. You control the cloning of the fields by annotating them in the entity class.

Just to get a flavour of it, here is an example class:

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
    }
}

More details here: https://github.com/mnorbi/fluidity-cloning

There is also a hibernate specific extension in case one needs it.

Empurple answered 9/4, 2013 at 19:28 Comment(0)
E
0

Use serialization and then deserialization, but be aware that this approach works only with Serializable classes without transient fields. Also, your singletons will not be singletons anymore.

Expressly answered 20/3, 2009 at 12:11 Comment(1)
The use of serialization and deserialization for the work of cloning memory objects at runtime, or between separate runs of a program is a bad idea. For more info on why this is, google: "why serialization and deserialization is bad".Righteousness

© 2022 - 2024 — McMap. All rights reserved.