Java的clone方法效率問題


  在Java中,經常會需要新建一個對象,很多情況下,需要這個新建的對象和現有的某個對象保持屬性一致。

  那么,就有兩種方式來實現這個對象的構造:

    ①通過新建一個對象,為這個對象的屬性根據原有對象的屬性來進行賦值

    ②調用clone方法,來實現實例對象的克隆

  對於Java的clone方法,需要注意的就是它實際上是一種“淺克隆”(Shallow Clone),對於int、double這種基本數據類型,直接拷貝值;對於String這樣的類對象,則是直接拷貝引用。因此對於通過clone得到的對象來說,它很大程度上還是和原有被克隆對象之間有着很大的聯系(例如修改clone對象的String屬性,則原有對象的String屬性也會變化)。

  於是,為了得到兩個完全“獨立”的具有相同屬性的實例對象,就涉及到“深克隆”(Deep Clone)。至於如何來編寫實現“深克隆”,可參考這篇博客(來自大學同學朱大佬分享): https://blog.csdn.net/zhangjg_blog/article/details/18369201

  當然,至此要說明的重點並不是如何實現“深克隆”和“淺克隆”,而是要比較一下上面提到的①②兩種方法,哪種效率更高一些。

  首先,對於“淺克隆”來進行測試:

  注:這里的SimpleTest類只包含一個name屬性(String型),而Test類則包括name以及line0~line9這11個屬性(均為String型)。這里讓這些屬性值在構造器中完成初始化。

  測試代碼:

  說明: 這里分別對四種情況進行測試:

    1). 簡單的“淺克隆”,通過用SimpleTest的“淺克隆”來進行測試 

    2). 簡單的“構造”,通過用SimpleTest的構造器來實例對象,其name值,直接通被克隆對象的name值來獲取

    3). 復雜的“淺克隆”,通過Test的“淺克隆”來進行測試

    4). 復雜的“構造”,通過正常構造Test,來獲得Test實例,需要注意的就是,此時Test的構造器內涉及到一系列的new操作(代表復雜構造操作)

  測試結果:

 

  結果說明: 

    ①對於輕量型的類對象,通過new操作來構造,效率會更高(對比987ms和7ms)。

    ②對於重量型的類對象,通過clone操作來進行構造,效率會更高(對比1016ms和4503ms)。

  原因分析:

    ①對於輕量型的類對象,通過new操作即可很快地進行構造,而clone方法依舊涉及到new操作,但其會進行更多的方法調用,執行流程會消耗一些時間

    ②對於重量型的類對象,new操作則需要構造相當多的對象,從而會消耗很多時間;但clone方法(這里是“淺克隆”)只需要拷貝引用給新的對象即可,因此消耗的時間會更少

 

  但是,這只是對於“淺克隆”來說,clone的構造效率會更高,但對於“深克隆”來說,情況卻並不樂觀。

  首先,按照前面提到的那篇博客,來進一步構造深克隆:

class Person implements Cloneable {
    public int age;
    public String name;
    public Body body;

    public Person(int age, String name, Body body) {
        this.age = age;
        this.name = name;
        this.body = body;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Person newPerson = (Person)super.clone();
        newPerson.body = (Body)body.clone();
        return newPerson;
    }
}

class Body implements Cloneable {
    public Head head;

    public Body(Head head) {
        this.head = head;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Body newBody = (Body)super.clone();
        newBody.head = (Head)head.clone();
        return newBody;
    }
}

class Head implements Cloneable {
    public Face face;

    public Head(Face face) {
        this.face = face;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Head newHead = (Head)super.clone();
        newHead.face = (Face)face.clone();
        return newHead;
    }

}

class Face implements Cloneable {
    public Mouth mouth;

    public Face(Mouth mouth) {
        this.mouth = mouth;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Face newFace = (Face)super.clone();
        newFace.mouth = (Mouth)mouth.clone();
        return newFace;
    }
}

class Mouth implements Cloneable {
    public Tooth tooth;

    public Mouth(Tooth tooth) {
        this.tooth = tooth;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Mouth newMouth = (Mouth)super.clone();
        newMouth.tooth = (Tooth)tooth.clone();
        return newMouth;
    }
}

class Tooth implements Cloneable {
    public final int number;

    public Tooth(int number) {
        this.number = number;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
Deep Clone

  測試代碼:

  

            // 測試Deep Clone/New 的測試代碼
            Person person1 = new Person(20, "Test", new Body(new Head(new Face(new Mouth(new Tooth(32))))));

            Person person2 = null;
            long startTime = System.currentTimeMillis();
            for(int i = 0; i < 100000000; i++) 
                person2 = (Person)person1.clone();
            long endTime = System.currentTimeMillis();
            System.out.println();
            System.out.println("Deep Clone : " + (endTime - startTime) + "ms");

            System.out.println();
            System.out.println("person1 == person2: " + (person1 == person2));
            System.out.println();

            System.out.println("person1.age == person2.age: " + (person1.age == person2.age));
            System.out.println("person1.name == person2.age: " + (person1.name == person2.name));
            System.out.println();

            System.out.println("person1.body == person2.body: " + (person1.body == person2.body));
            System.out.println("person1.body.head == person2.body.head: " + (person1.body.head == person2.body.head));
            System.out.println("person1.body.head.face == person2.body.head.face: " + (person1.body.head.face == person2.body.head.face));
            System.out.println("person1.body.head.face.mouth == person2.body.head.face.mouth: " 
                    + (person1.body.head.face.mouth == person2.body.head.face.mouth));
            System.out.println("person1.body.head.face.mouth.tooth == person2.body.head.face.mouth.tooth: " 
                    + (person1.body.head.face.mouth.tooth == person2.body.head.face.mouth.tooth));
            System.out.println("person1.body.head.face.mouth.tooth.number == person2.body.head.face.mouth.tooth.number: " 
                    + (person1.body.head.face.mouth.tooth.number == person2.body.head.face.mouth.tooth.number));
            System.out.println();

            Person person3 = null;
            startTime = System.currentTimeMillis();
            for(int i = 0; i < 100000000; i++) {
                Tooth tooth = new Tooth(person1.body.head.face.mouth.tooth.number);
                Mouth mouth = new Mouth(tooth);
                Face face = new Face(mouth);
                Head head = new Head(face);
                Body body = new Body(head);
                person3 = new Person(20, "Test", body);
            }
            endTime = System.currentTimeMillis();
            System.out.println("Deep New : " + (endTime - startTime) + "ms");

            System.out.println();
            System.out.println("person1 == person3: " + (person1 == person3));
            System.out.println();

            System.out.println("person1.age == person3.age: " + (person1.age == person3.age));
            System.out.println("person1.name == person3.age: " + (person1.name == person3.name));
            System.out.println();

            System.out.println("person1.body == person3.body: " + (person1.body == person3.body));
            System.out.println("person1.body.head == person3.body.head: " + (person1.body.head == person3.body.head));
            System.out.println("person1.body.head.face == person3.body.head.face: " + (person1.body.head.face == person3.body.head.face));
            System.out.println("person1.body.head.face.mouth == person3.body.head.face.mouth: " 
                    + (person1.body.head.face.mouth == person3.body.head.face.mouth));
            System.out.println("person1.body.head.face.mouth.tooth == person3.body.head.face.mouth.tooth: " 
                    + (person1.body.head.face.mouth.tooth == person3.body.head.face.mouth.tooth));
            System.out.println("person1.body.head.face.mouth.tooth.number == person3.body.head.face.mouth.tooth.number: " 
                    + (person1.body.head.face.mouth.tooth.number == person3.body.head.face.mouth.tooth.number));
            System.out.println();
完整測試代碼

 

  按照這個結構,深克隆得到的對象和原有對象的關系如圖:

 

  測試結果:

  

  注: 這里的person3是通過new來構造的對象,其內部包含的每個引用對象(不包括其name),均是通過new來進行構造的。

  結論: 可以發現,“深克隆”並沒有提高克隆的效率!相反,這種方法此時比通過new構造對象的方法效率還低。

  原因分析: 就和上面的測試一樣: 此處的“深克隆”依舊會通過new的方法來不斷構造對象,因而本質上並沒有提高效率(不似“淺克隆”一般直接復制引用即可),反而由於操作流程的層層調用,使得其執行速度不如new構造對象的方法快。

 

  至此,基本可以確定,clone方法只有在進行復雜的“淺克隆”時效率才會明顯高於new構造方式,但由於此時的克隆本質上依舊是“淺克隆”,因此需要注意引用的指向問題,避免錯誤更改關聯的信息值。

  但是,對於測試一來說,這個比較是不全面的。因為淺克隆本質上,只是把類中的引用復制到新的對象屬性中,而該測試中,new構造的對象的內部引用則是完全“獨立”的。

  因此,為了測試clone方法的“淺克隆”方式是否真正會提高效率,還要進行測試:

  這里,更改了Test類的代碼,為Test新增了一種構造器,便於構造對象;為Test新增了copy方法,該方法只是構建一個新的Test對象,並將自身的所有屬性引用拷貝到這個新的對象屬性中。

  代碼如圖:

  測試代碼: 

  說明: 在原有測試代碼的基礎上,新增了測試對象t4,該對象的屬性值通過調用copy方法來獲得。當然,這里的copy實現的就是“淺克隆”的過程,即僅僅復制引用。

  最后會打印三行信息:反映對於類內屬性是否為真正的克隆。如果返回值為true,則表明line0引用指向的是相同空間,為“淺克隆”;如果返回值為false,則表明為真克隆,即對象真正隔離。

  測試結果:

  結論: 對於復雜的“淺克隆”,通過Copy的方式可實現類似Clone的實現(可以看到,Copy方法和Clone方法都是將引用直接拷貝),但是效率並沒有Clone高(多次測試都是如此),這才是真正體現了Clone方法效率高的測試。

  

  需要注意的是,clone盡管默認是“淺克隆”,但是依舊有很多應用情況。clone方法,可以將原有對象的private屬性的信息也克隆下來,需要對此注意。

  通過查閱stackoverflow,可以看到更多關於這方面的內容,其中也包括clone方法的應用場合(例如GUI面板等)。

  最后,附上這篇博客的所有代碼:

class Test implements Cloneable {
    public String name;
    // 10 lines(line0 ~ line9)
    public String line0;
    public String line1;
    public String line2;
    public String line3;
    public String line4;
    public String line5;
    public String line6;
    public String line7;
    public String line8;
    public String line9;
    
    public Test(String name) {
        this.name = name;
        // New the 10 lines
        line0 = new String("line0");
        line1 = new String("line1");
        line2 = new String("line2");
        line3 = new String("line3");
        line4 = new String("line4");
        line5 = new String("line5");
        line6 = new String("line6");
        line7 = new String("line7");
        line8 = new String("line8");
        line9 = new String("line9");
    }

    public Test() { } // 為測試三引入的構造器

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    public Test copy() { // 為測試三引入的“復制”方法
        Test ret = new Test();
        ret.name = this.name;
        ret.line0 = this.line0;
        ret.line1 = this.line1;
        ret.line2 = this.line2;
        ret.line3 = this.line3;
        ret.line4 = this.line4;
        ret.line5 = this.line5;
        ret.line6 = this.line6;
        ret.line7 = this.line7;
        ret.line8 = this.line8;
        ret.line9 = this.line9;
        return ret;
    }
}

class SimpleTest implements Cloneable {
    public String name;
    
    public SimpleTest(String name) {
        this.name = name;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

class Person implements Cloneable {
    public int age;
    public String name;
    public Body body;

    public Person(int age, String name, Body body) {
        this.age = age;
        this.name = name;
        this.body = body;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Person newPerson = (Person)super.clone();
        newPerson.body = (Body)body.clone();
        return newPerson;
    }
}

class Body implements Cloneable {
    public Head head;

    public Body(Head head) {
        this.head = head;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Body newBody = (Body)super.clone();
        newBody.head = (Head)head.clone();
        return newBody;
    }
}

class Head implements Cloneable {
    public Face face;

    public Head(Face face) {
        this.face = face;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Head newHead = (Head)super.clone();
        newHead.face = (Face)face.clone();
        return newHead;
    }

}

class Face implements Cloneable {
    public Mouth mouth;

    public Face(Mouth mouth) {
        this.mouth = mouth;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Face newFace = (Face)super.clone();
        newFace.mouth = (Mouth)mouth.clone();
        return newFace;
    }
}

class Mouth implements Cloneable {
    public Tooth tooth;

    public Mouth(Tooth tooth) {
        this.tooth = tooth;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Mouth newMouth = (Mouth)super.clone();
        newMouth.tooth = (Tooth)tooth.clone();
        return newMouth;
    }
}

class Tooth implements Cloneable {
    public final int number;

    public Tooth(int number) {
        this.number = number;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

public class T {
    public static void main(String[] args) {
        try {
            // 測試Deep Clone/New 的測試代碼
            Person person1 = new Person(20, "Test", new Body(new Head(new Face(new Mouth(new Tooth(32))))));

            Person person2 = null;
            long startTime = System.currentTimeMillis();
            for(int i = 0; i < 100000000; i++) 
                person2 = (Person)person1.clone();
            long endTime = System.currentTimeMillis();
            System.out.println();
            System.out.println("Deep Clone : " + (endTime - startTime) + "ms");

            System.out.println();
            System.out.println("person1 == person2: " + (person1 == person2));
            System.out.println();

            System.out.println("person1.age == person2.age: " + (person1.age == person2.age));
            System.out.println("person1.name == person2.age: " + (person1.name == person2.name));
            System.out.println();

            System.out.println("person1.body == person2.body: " + (person1.body == person2.body));
            System.out.println("person1.body.head == person2.body.head: " + (person1.body.head == person2.body.head));
            System.out.println("person1.body.head.face == person2.body.head.face: " + (person1.body.head.face == person2.body.head.face));
            System.out.println("person1.body.head.face.mouth == person2.body.head.face.mouth: " 
                    + (person1.body.head.face.mouth == person2.body.head.face.mouth));
            System.out.println("person1.body.head.face.mouth.tooth == person2.body.head.face.mouth.tooth: " 
                    + (person1.body.head.face.mouth.tooth == person2.body.head.face.mouth.tooth));
            System.out.println("person1.body.head.face.mouth.tooth.number == person2.body.head.face.mouth.tooth.number: " 
                    + (person1.body.head.face.mouth.tooth.number == person2.body.head.face.mouth.tooth.number));
            System.out.println();

            Person person3 = null;
            startTime = System.currentTimeMillis();
            for(int i = 0; i < 100000000; i++) {
                Tooth tooth = new Tooth(person1.body.head.face.mouth.tooth.number);
                Mouth mouth = new Mouth(tooth);
                Face face = new Face(mouth);
                Head head = new Head(face);
                Body body = new Body(head);
                person3 = new Person(20, "Test", body);
            }
            endTime = System.currentTimeMillis();
            System.out.println("Deep New : " + (endTime - startTime) + "ms");

            System.out.println();
            System.out.println("person1 == person3: " + (person1 == person3));
            System.out.println();

            System.out.println("person1.age == person3.age: " + (person1.age == person3.age));
            System.out.println("person1.name == person3.age: " + (person1.name == person3.name));
            System.out.println();

            System.out.println("person1.body == person3.body: " + (person1.body == person3.body));
            System.out.println("person1.body.head == person3.body.head: " + (person1.body.head == person3.body.head));
            System.out.println("person1.body.head.face == person3.body.head.face: " + (person1.body.head.face == person3.body.head.face));
            System.out.println("person1.body.head.face.mouth == person3.body.head.face.mouth: " 
                    + (person1.body.head.face.mouth == person3.body.head.face.mouth));
            System.out.println("person1.body.head.face.mouth.tooth == person3.body.head.face.mouth.tooth: " 
                    + (person1.body.head.face.mouth.tooth == person3.body.head.face.mouth.tooth));
            System.out.println("person1.body.head.face.mouth.tooth.number == person3.body.head.face.mouth.tooth.number: " 
                    + (person1.body.head.face.mouth.tooth.number == person3.body.head.face.mouth.tooth.number));
            System.out.println();

            // 測試Shallow Clone/New的測試代碼
            SimpleTest st1 = new SimpleTest("SimpleTest");
            SimpleTest st2 = null;
            startTime = System.currentTimeMillis();
            for(int i = 0; i < 100000000; i++) 
                st2 = (SimpleTest)st1.clone();
            endTime = System.currentTimeMillis();
            System.out.println("Simple Shallow Clone : " + (endTime - startTime) + "ms");

            SimpleTest st3 = null;
            startTime = System.currentTimeMillis();
            for(int i = 0; i < 100000000; i++) 
                st3 = new SimpleTest(st1.name);
            endTime = System.currentTimeMillis();
            System.out.println("Simple Shallow New : " + (endTime - startTime) + "ms");

            Test t1 = new Test("Test");
            Test t2 = null;
            startTime = System.currentTimeMillis();
            for(int i = 0; i < 100000000; i++) 
                t2 = (Test)t1.clone();
            endTime = System.currentTimeMillis();
            System.out.println("Complex Shallow Clone : " + (endTime - startTime) + "ms");

            Test t3 = null;
            startTime = System.currentTimeMillis();
            for(int i = 0; i < 100000000; i++) 
                t3 = new Test(t1.name);
            endTime = System.currentTimeMillis();
            System.out.println("Complex Shallow New : " + (endTime - startTime) + "ms");

            Test t4 = null;
            startTime = System.currentTimeMillis();
            for(int i = 0; i < 100000000; i++) 
                t4 = t1.copy();
            endTime = System.currentTimeMillis();
            System.out.println("Complex Shallow Copy : " + (endTime - startTime) + "ms");

            System.out.println("t1.line0 == t2.line0 : " + (t1.line0 == t2.line0));
            System.out.println("t1.line0 == t3.line0 : " + (t1.line0 == t3.line0));
            System.out.println("t1.line0 == t4.line0 : " + (t1.line0 == t4.line0));

        } catch(CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}
T.java

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM