Java Cloning VS Constructor Copy

From time to time I find myself puzzled with what would seem to be common knowledge in java programming. Recently a question came up regarding the most efficient way to create copies of objects.

Naturally you would think that if its the performance you are after you should use Object.clone(). However the implemntation of that method is not a trivial task. In fact if you look on the stack overflow there are heated debates e.g. here or here.

Josh Bloch, author of the java Collections framework, in fact debates that the whole Clonable implementation is broken in java.

Some of the most anoying thing are that Clonable is a marker interface, which does not give anything to the API user. Object.clone() throws exception and use of these object in generics is very cumbersome. Not to mention the deep VS shallow copy of objects and absolutely oblivious users that use clone() methods - in other words there is no guarantee what they get from your implementation.

So after all these drawback what is the point of using this?

Well this is obvious - performance! But is it, really?

Lets look at couple of scenarious.

Scenario 1: Clonable VS Constructor copy for object with 10 immutable members and 10 objects.

public class ClonableBasicImpl implements Cloneable {

    private String prop1;
    private String prop2;
    private String prop3;
    private String prop4;
    private String prop5;
    private String prop6;
    private String prop7;
    private String prop8;
    private String prop9;
    private String prop10;

    public ClonableBasicImpl() {
    }

... getters/setters

    @Override
    public Object clone() throws CloneNotSupportedException {

        final ClonableBasicImpl clone = (ClonableBasicImpl) super.clone(); // shallow copy for immutable

        return clone;
    }
}

public class NonClonableBasicImpl {

    private String prop1;
    private String prop2;
    private String prop3;
    private String prop4;
    private String prop5;
    private String prop6;
    private String prop7;
    private String prop8;
    private String prop9;
    private String prop10;

    public NonClonableBasicImpl() {
    }

... getters/setters

    private NonClonableBasicImpl(NonClonableBasicImpl nonClonable) {
        prop1 = nonClonable.prop1;
        prop2 = nonClonable.prop2;
        prop3 = nonClonable.prop3;
        prop4 = nonClonable.prop4;
        prop5 = nonClonable.prop5;
        prop6 = nonClonable.prop6;
        prop7 = nonClonable.prop7;
        prop8 = nonClonable.prop8;
        prop9 = nonClonable.prop9;
        prop10 = nonClonable.prop10;
    }

    public NonClonableBasicImpl copy() {
        return new NonClonableBasicImpl(this);
    }

}

 

Running trial runs with Caliper on sample sizes 5000, 7500 and 10000 copies produces the following data

 

Scenario 2: Clonable VS Constructor copy for object with 10 immutable members and 10 objects.

public class ClonableImpl implements Cloneable {

    private String prop1;
    private String prop2;
    private String prop3;
    private String prop4;
    private String prop5;
    private String prop6;
    private String prop7;
    private String prop8;
    private String prop9;
    private String prop10;

    private ClonableImpl obj1;
    private ClonableImpl obj2;
    private ClonableImpl obj3;
    private ClonableImpl obj4;
    private ClonableImpl obj5;
    private ClonableImpl obj6;
    private ClonableImpl obj7;
    private ClonableImpl obj8;
    private ClonableImpl obj9;
    private ClonableImpl obj10;

    public ClonableImpl() {
    }

... getters/setters

    @Override
    public Object clone() throws CloneNotSupportedException {

        final ClonableImpl clone = (ClonableImpl) super.clone(); // shallow copy for immutable

        // deep copy for objects
        clone.obj1 = this.obj1 != null ? (ClonableImpl) this.obj1.clone() : null;
        clone.obj2 = this.obj2 != null ? (ClonableImpl) this.obj2.clone() : null;
        clone.obj3 = this.obj3 != null ? (ClonableImpl) this.obj3.clone() : null;
        clone.obj4 = this.obj4 != null ? (ClonableImpl) this.obj4.clone() : null;
        clone.obj5 = this.obj5 != null ? (ClonableImpl) this.obj5.clone() : null;
        clone.obj6 = this.obj6 != null ? (ClonableImpl) this.obj6.clone() : null;
        clone.obj7 = this.obj7 != null ? (ClonableImpl) this.obj7.clone() : null;
        clone.obj8 = this.obj8 != null ? (ClonableImpl) this.obj8.clone() : null;
        clone.obj9 = this.obj8 != null ? (ClonableImpl) this.obj9.clone() : null;
        clone.obj10 = this.obj10 != null ? (ClonableImpl) this.obj10.clone() : null;

        return clone;
    }
}

public class NonClonableImpl {

    private String prop1;
    private String prop2;
    private String prop3;
    private String prop4;
    private String prop5;
    private String prop6;
    private String prop7;
    private String prop8;
    private String prop9;
    private String prop10;

    private NonClonableImpl obj1;
    private NonClonableImpl obj2;
    private NonClonableImpl obj3;
    private NonClonableImpl obj4;
    private NonClonableImpl obj5;
    private NonClonableImpl obj6;
    private NonClonableImpl obj7;
    private NonClonableImpl obj8;
    private NonClonableImpl obj9;
    private NonClonableImpl obj10;

    public NonClonableImpl() {
    }

... getters/setters

    private NonClonableImpl(NonClonableImpl nonClonable) {
        prop1 = nonClonable.prop1;
        prop2 = nonClonable.prop2;
        prop3 = nonClonable.prop3;
        prop4 = nonClonable.prop4;
        prop5 = nonClonable.prop5;
        prop6 = nonClonable.prop6;
        prop7 = nonClonable.prop7;
        prop8 = nonClonable.prop8;
        prop9 = nonClonable.prop9;
        prop10 = nonClonable.prop10;

        obj1 = nonClonable.obj1 != null ? nonClonable.obj1.copy() : null;
        obj2 = nonClonable.obj2 != null ? nonClonable.obj2.copy() : null;
        obj3 = nonClonable.obj3 != null ? nonClonable.obj3.copy() : null;
        obj4 = nonClonable.obj4 != null ? nonClonable.obj4.copy() : null;
        obj5 = nonClonable.obj5 != null ? nonClonable.obj5.copy() : null;
        obj6 = nonClonable.obj6 != null ? nonClonable.obj6.copy() : null;
        obj7 = nonClonable.obj7 != null ? nonClonable.obj7.copy() : null;
        obj8 = nonClonable.obj8 != null ? nonClonable.obj8.copy() : null;
        obj9 = nonClonable.obj9 != null ? nonClonable.obj9.copy() : null;
        obj10 = nonClonable.obj10 != null ? nonClonable.obj10.copy() : null;
    }

    public NonClonableImpl copy() {
        return new NonClonableImpl(this);
    }

}

 

Running trial runs with Caliper on sample sizes 5000, 7500 and 10000 copies produces the following data

 

Conclusion:

Results from Caliper benchmarks show that for simple immutable member copying ther is no significant evidence of any difference in performance. For objects with mutable members that also require cloning, objects with 10 such members will only get up to 5% performance increase. Taking into accont that such operations (cloning/prototyping) are not major part of the systems as most would instantiate classes anyway this really is very insignificant.

This conclusion leads me to believe that for non mission critical code there is no benefit at all in using Clonable. It is absolutely fine to use Constructor copy. However the choice of approach largerly depends on the requirements of your system.

 

Source code for these benchmarks can be downloaded here

EDIT: I have put the sandbox onto github so latest sources are there

This page was last updated on: 27/03/2014 14:01