Site Search :
Standard Enterprise XML Methodology Pattern Setting Tunning Other
Article Contributors
GuestBook
Javapattern Maven
XSourceGen Dev
JetSpeed Test
JLook Image
jLook Family Site


Deep Copy, Shallow Copy에 관한 Perfomance Test 및 고려사항
 
객체를 복사하는 방법에 있어서 성능에 관련된 이슈를 테스트 해본다. 트리, 그래프등의 자료구조에서 이슈가 될만한 내용을 프로그램으로 구현해보도록 한다. ( 2003/03/10 ) 151
Written by ienvyou - 최지웅
1 of 1
 

MAKING DEEP COPIES OF OBJECTS

여기서는 "shallow" copy라 불리는 Object.clone 과 자신의 clone()메소드 또는 사용자정의 clone() 조합에 의한 object copy를 위한 방법을 정의하겠다

예제로는 ArrayList객체를 사용한다.
clone메소드를 사용하기 위해 새로운 ArrayList를 생성하게 될것이며, 실제 원본 리스트의 다양한 reference를 copy하게 될것이다.

각각의 원소가 변하기 쉽거나, 변할수 있는 상황이라면 기존의 원본 list들은 새로운 리스트측으로 복사되어야 할 것이다. 다른 말로 표현하자면 두개의 ArrayList객체가  공유 references를 통해 공통원소를 가지고 있는것이다.
이는 그냥 공유가 되는 것이 아니라 list를 copy하고자 할때이며,  만약 list를 copy하기를 원한다면 두개의 리스트에 대한 공유를 갱신시키면 안된다.

이러한 다른 복사(clone등)들과 구별되는 총체적인 내용을 "deep copies"라고 부른다.
아래는 deep copy에 대한 테크니컬한 프로그램을 보여준다.

각각의 copy를 50000번 반복시도 해보겠다.

    import java.io.*;
    import java.util.*;
   
    class A implements java.io.Serializable {
        private int x;
   
        public A(int i) {
            setX(i);
        }
        public void setX(int i) {
            x = i;
        }
        public int getX() {
            return x;
        }
    }
   
    public class DeepDemo {
   
        // shallow copy를 정의했다.
        static ArrayList copy1(ArrayList list) {
            return (ArrayList)list.clone();
        }
   
        // deep copy를 직접 작성한 것이다.
        static ArrayList copy2(ArrayList list) {
            ArrayList newlist = new ArrayList();
            A aobj = (A)list.get(0);
            newlist.add(new A(aobj.getX()));
            return newlist;
        }
   
        //  serialization를 통한 deep copy를 보여준다.
        static ArrayList copy3(ArrayList list) throws Exception {
   
            // 직렬화된 ArrayList를 byte array로 넣는다
   
            ByteArrayOutputStream baos =
                new ByteArrayOutputStream(100);
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(list);
            byte buf[] = baos.toByteArray();
            oos.close();
   
            // deserialize시킨 byte array를  ArrayList로 넣는다.
   
            ByteArrayInputStream bais =
                new ByteArrayInputStream(buf);
            ObjectInputStream ois = new ObjectInputStream(bais);
            ArrayList newlist = (ArrayList)ois.readObject();
            ois.close();
   
            return newlist;
        }
   
        static final int NUMITERS = 50000;
   
        public static void main(String args[]) throws Exception {
            ArrayList listold, listnew;
            long currtime, elapsedtime2, elapsedtime3;
   
            // shallow copy를 시작한다.
   
            listold = new ArrayList();
            listold.add(new A(37));
            listnew = copy1(listold);
            ((A)listold.get(0)).setX(47);
            System.out.print("copy1 old/new = ");
            System.out.print(((A)listold.get(0)).getX() + " ");
            System.out.println(((A)listnew.get(0)).getX());
   
            // 직접 코딩을 통한 deep copy method의 호출
   
            listold = new ArrayList();
            listold.add(new A(37));
            currtime = System.currentTimeMillis();
            for (int i = 1; i <= NUMITERS; i++) {
                listnew = copy2(listold);
            }
            elapsedtime2 = System.currentTimeMillis() - currtime;
            ((A)listold.get(0)).setX(47);
            System.out.print("copy2 old/new = ");
            System.out.print(((A)listold.get(0)).getX() + " ");
            System.out.println(((A)listnew.get(0)).getX());
   
            // 직렬화를 통한 deep copy
   
            listold = new ArrayList();
            listold.add(new A(37));
            currtime = System.currentTimeMillis();
            for (int i = 1; i <= NUMITERS; i++) {
                listnew = copy3(listold);
            }
            elapsedtime3 = System.currentTimeMillis() - currtime;
            ((A)listold.get(0)).setX(47);
            System.out.print("copy3 old/new = ");
            System.out.print(((A)listold.get(0)).getX() + " ");
            System.out.println(((A)listnew.get(0)).getX());
   
            System.out.println();
            System.out.println("copy2 time = " + elapsedtime2);
            System.out.println("copy3 time = " + elapsedtime3);
        }
    }

위의 프로그램은 ArrayList가 A타입의 원소하나를 가지고 사용한 프로그램이다. A타입의 객체는 setX() 메소드를 통하여 값이 변하게 된다.

이 프로그램은 3가지의 copy메소드를 보여주고 있는데 copy1은 ArrayList.clone()메소드를 이용한 shallow copy를 보여주고 있다.
이 메소드는 새로운 리스트를 생성한후에 기존의 list가 참조하고 있는 object를 복사한다.

이후에 copy1메소드가 호출되는데, 프로그램에서는 old list의 한 원소의 값이 바뀌고, 새로운 list안의 원소값을 체크함으로서 그 새로운 list의 값이 바뀌게끔 코딩하였다.여기서의 새로운 list로 변하는 것은 shallow copy(clone메소드를 통한)를 가르킨다.

copy2메소드는 deep copy를 만들기 위해 직접 작성한 코드를 이용하여 접근을 시도하고 있는데, 새로운 ArrayList를 준비하고 기존의 list로부터 원소값을 얻어온후에 새로운 list에 기존에 생성된 원소값을 직접 add시키는 것을 보여주고 있다.

copy3에서는 serialization을 사용했다. ArrayList를 byte array로 변환한 후에 ByteArrayOutputStream object를 이용하여 저장시킨후, 다시 역으로 프로세스를 돌려 ArrayList로 값을 얻어내었다.

이 프로그램을 실행시켰을때 아래의 결과값을 확인할수 있다.

    copy1 old/new = 47 47
    copy2 old/new = 47 37
    copy3 old/new = 47 37

    copy2 time = 47
    copy3 time = 8500

hand-coding한 deep copying인 copy2와 serialization을 사용한 copy3는 확연히 구분이 되고 있다. 여기서의 perfomance는 명백한 issue이다.
serialization접근은 hand-coding한것에 비해 눈에 띄게 느린것을 확인할수 있을것이다.
어쨌든 deep copies를 할때는 serialization을 피하는 것이 바람직할것이다.


위 코드를 보면 알겠지만 시도횟수는 50000번이다. 하나의 객체에 대한 copy만 하므로 그리 오랜 시간이 걸리지 않지만, 이러한 시간은 copy를 이용하여 애플리케이션의 critical한 요소에는 적용을 시킬수 없을 것이다.

여기서 가장 큰 이슈를 항상 그래왔듯이 성능이다.트리구조나 그래프 등의 자료구조에 있어서 deep copy를 사용하기 원한다면 프로그램이 너무 복잡해질수 있다

또한 serialization에 의 한 접근은 그 비용과 시간에 있어서 고려해보아야 할 문제이다.


 

Written by Carouser : 2001-05-30
 
1
References
 
Copyright ⓒ 2003 www.javapattern.info & www.jlook.com, an jLOOK co.,LTD