deep copy, shallow copy
자바의 배열, collection을 다룰 때 값을 복사하는 작업을 수행해야할 경우가 있다. 이때 유의해야할 점은 java는 두가지 방법의 복사방법이 있다는 것이다.
- 얕은 복사(shallow copy) : 메모리 참조 주소를 복사한다.
- 깊은 복사(deep copy) : 실제 값 자체를 메모리에 복사한다.
예시를 통해 두가지 복사 방법을 정리해보자
shallow copy
shallow copy는 값이 아닌 주소 값을 복사한다
- 복사 후 원본이 달라지면 복사한 값도 같이 달라진다(같은 주소를 바라보기 때문)
- equal(=) 연산자는 얕은 복사를 수행한다.
- Cloneable 인터페이스의 clone이 구현되지 않은 Custom 객체의 .clone()
import java.util.ArrayList;
import java.io.*;
public class shallow_deep {
public static void main(String[] args) {
//shallow copy origin
ArrayList<Integer> origin = new ArrayList<>();
origin.add(1);
origin.add(2);
origin.add(3);
System.out.println("원본 출력");
origin.forEach(i -> System.out.println(i)); // 1, 2, 3
//shallow copy target
ArrayList<Integer> target = origin;
//change original
origin.set(0,4);
origin.set(1,5);
origin.set(2,6);
System.out.println("얕은 복사본 출력");
target.forEach(i -> System.out.println(i));// 4, 5, 6
}
}
//Cloneable 인터페이스의 clone이 구현되지 않은 Custom객체의 .clone()
//Clone
import java.util.ArrayList;
public class shallow_clone {
static class Shallow{
int value;
public Shallow(int value){
this.value = value;
}
}
public static void main(String[] args) {
ArrayList<Shallow> origin = new ArrayList<>();
origin.add(new Shallow(1));
origin.add(new Shallow(2));
origin.add(new Shallow(3));
//clone()
ArrayList<Shallow> target = (ArrayList<Shallow>) origin.clone();
//change origin value
origin.get(0).value = 9; // 1 -> 9
System.out.println("origin");
origin.forEach(shallow -> System.out.println(shallow.value)); // 9 2 3
System.out.println("shallow");
target.forEach(shallow -> System.out.println(shallow.value)); // 9 2 3
}
}
deep copy
- 객체를 복사할 때 해당 객체를 완전히 메모리에 복사한다(주소 x)
- Cloneable 인터페이스의 clone이 구현된 객체의 .clone()
- deep copy에 대한 api 가 존재하지 않아 개발자가 직접 구현해야함
import java.util.ArrayList;
public class deepcopy {
static class Deep implements Cloneable{
String str;
public Deep(String str){
this.str = str;
}
@Override
protected Deep clone() throws CloneNotSupportedException{
return (Deep) super.clone();
}
}
public static void main(String[] args) {
//origin
Deep origin = new Deep("origin");
Deep target;
//clone
try {
target = origin.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
origin.str = "change";
System.out.println(origin.str); //change
System.out.println(target.str); //origin
//addAll()
ArrayList<Deep> deepArray = new ArrayList<>();
ArrayList<Deep> targetArray = new ArrayList<>();
deepArray.add(new Deep("first"));
deepArray.add(new Deep("second"));
deepArray.add(new Deep("third"));
System.out.println("==========");
targetArray.addAll(deepArray); // addAll 도 얕은 복사 수행함
targetArray.get(2).str = "change";
System.out.println("origin array:");
deepArray.forEach(deep -> System.out.println(deep.str));// first second change
System.out.println("target array:");
targetArray.forEach(deep -> System.out.println(deep.str));// first second change
//deep copy implements
ArrayList<Deep> copyArray = new ArrayList<>();
for(Deep deep : deepArray){
try {
copyArray.add(deep.clone()); // 복사 수행
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
copyArray.get(2).str = "copied!";
System.out.println("========");
System.out.println("origin array:");
deepArray.forEach(deep -> System.out.println(deep.str)); //first second change
System.out.println("deep copy array:");
copyArray.forEach(deep -> System.out.println(deep.str)); //first second copied!
}
}
[참고]
https://yeonyeon.tistory.com/122
https://gwbb.tistory.com/16
https://jaehun2841.github.io/2019/01/13/java-object-copy/#Shallow-Copy