본문 바로가기

JAVA

JAVA 객체 복사 방식 (깊은 복사 vs 얕은 복사)

왠만하면 프로젝트가 어느 정도 마무리되고 난 후 배운 것들을 블로그에 정리하는데, 이건 정말 모르고 사용하면 꽤나 고생할 것 같아서(이미 고생하긴 했다....;;) 까먹기 전에 정리해두려고 한다.

 

객체를 복사할 때 어떤 방식을 사용하는가?

 

보통 자바에서 객체를 복사/복제(clone)을 한다고 하면, 다음과 같이 쓰곤 한다.

CustomClass object = new CustomClass();
CustomClass copied = object;

위의 예시는 안전한 복사가 아니다 (경우에 따라).

 

 

ArrayList를 예시로 들어보겠다.

 

리스트 복사! 하면 가장 먼저 떠오르는 방식이 일반적으로 아래와 같은 방식일 것이다.

ArrayList<String> classList = new ArrayList<>();
classList.add("a");
classList.add("b");
classList.add("c");
classList.add("d");

ArrayList<CustomClass> copiedList = new ArrayList<>();

copiedList = classList;		//복사?

이렇게 a = b 꼴 형식으로 대입해버리면 어떤 문제가 발생한다.

 

결론부터 말하자면 이 복사 방식은 얕은 복사라고 불리며, 한 쪽 리스트에 대한 접근이 다른 한 쪽에도 영향을 미친다.

...
copiedList = classList;		//얕은 복사

classList.add("e");		//오리지널 리스트에 아이템 추가

위와 같은 예시에서 copiedList와 classList에는 둘 다 a, b, c, d 라는 원소를 가지고 있다.

 

다만 classList에서 "e"라는 원소를 추가하면, copiedList에도 "e"가 똑같이 추가된다.

...
copiedList.clear();			//초기화

마찬가지로 복사본을 초기화하면 원본 리스트도 초기화된다.

 

한마디로 정리하면, 이 방식은 주소값을 복사하는 것이기 때문에 얕은 복사에서는 원본과 사본 ArrayList가 완벽하게 동기화된다.

 

참고로 맨 위의 객체 복사도 얕은 복사에 해당한다.

 

 

반대로 깊은 복사(deep copy)라는 것이 있다.

 

이 복사 방식은 원본으로부터 item 자체만 복사해온다.

 

즉, 복사 이후에 서로에게 주는 영향은 일체 없다. (단, Class가 또 다른 Class를 포함한다면 말이 달라진다.)

 

깊은 복사를 사용하는 방식은 다음과 같다.

public class CustomClass implements Cloneable{
	...
    @Override
    public Object clone(){
    	//CloneNotSupportedException 처리
    	return super.clone();
    }
}

CustomClass object = new CustomClass();
CustomClass copied = (CustomClass) object.clone();

우선 해당 객체의 클래스에 Cloneable 인터페이스를 implement해주고 unimplemented function인 clone()를 선언해주면 위와 같이 사용할 수 있다.

 

ArrayList의 경우 깊은 복사는 다음과 같이 적용 가능하다.

copiedList.addAll(classList);

 

뭔가 깊은 복사도 조건에 따라서 완전한 깊은 복사가 아닌 것 같은데 좀 더 공부해보고 내용을 추가하겠음.