Java로 백준 알고리즘 문제를 풀다보면 배열을 복사할 때가 있습니다
이때, 얕은 복사와 깊은 복사의 개념을 모르고 단순히 '='로 복사하면
출력할 때 잘못된 결과를 얻을 수 있습니다.
이런 실수를 반복하지 않기 위해 개념을 정리하고자 글을 작성했습니다
해당 글에서 Stack과 Heap영역을 비교하는 내용은 생략했습니다.
Java의 얕은 복사는 주소 값을 복사합니다.
보통 객체를 '='으로 복사하면 얕은 복사가 일어납니다
public class Main {
public static void main(String[] args){
int[] test1 = {1,2,3};
int[] test2 = test1;
System.out.println("test1: " + test1 + " " + Arrays.toString(test1));
System.out.println("test2: " + test2 + " " + Arrays.toString(test1));
System.out.println();
test2[0] = 7;
System.out.println("test1: " + test1 + " " + Arrays.toString(test1));
System.out.println("test2: " + test2 + " " + Arrays.toString(test2));
}
}
위와같이 테스트코드를 작성했습니다.
출력결과는 다음과 같습니다
두 배열이 동일한 주소값을 가지고 있습니다
test2의 0번 인덱스 값을 변경하면 1번 인덱스 값도 변경됩니다
동일한 주소를 가리키고 있기 때문에 위와같이 동작합니다
Java의 깊은 복사는 데이터 자체를 복사해서 다른 주소값을 가집니다.
보통 clone() 메소드를 사용하면 깊은 복사가 일어납니다
public class Main {
public static void main(String[] args) {
int[] test1 = {1,2,3};
int[] test2 = test1.clone();
System.out.println("test1: " + test1 + " " + Arrays.toString(test1));
System.out.println("test2: " + test2 + " " + Arrays.toString(test1));
System.out.println();
test2[0] = 7;
System.out.println("test1: " + test1 + " " + Arrays.toString(test1));
System.out.println("test2: " + test2 + " " + Arrays.toString(test1));
}
}
이번에는 clone() 메소드를 사용해서 위와 같이 작성했습니다
출력결과는 다음과 같습니다
깊은 복사가 일어나며 test1의 값이 test2로 복사되었지만, 두 배열의 주소는 다릅니다.
따라서 test2의 0번 인덱스 값을 변경해도 test1의 값은 변경되지 않습니다
복사한 1차원 배열의 데이터 수정이 원본 배열에 영향을 주지 않아야 한다면
clone() 메소드를 사용해서 깊은 복사를 해야합니다.
2차원 배열은 clone() 메소드를 사용해도, 변경된 값이 원본 배열에 반영됩니다
아래와 같은 테스트코드를 작성했습니다. (출력코드 생략)
public class Main {
public static void main(String[] args){
int[][] test1 = {{1,2,3}, {4,5,6}, {7,8,9}};
int[][] test2 = test1.clone();
test2[0][0] = 0;
}
}
출력결과는 다음과 같습니다
데이터가 아닌 각 test[i]의 주소가 복사된 것을 확인할 수 있습니다
따라서 test2의 값을 변경하면 test1의 값도 변경됩니다
그렇다면 왜 2차원 배열은 1차원 배열과 다르게 clone()메소드를 사용해도 깊은 복사가 되지 않을까요?
2차원 배열의 형태를 잘 살펴보면 정답을 찾을 수 있습니다
2차원 배열의 값이 있는 곳은 test[i][j]입니다.
즉, i번째 주소에서 j의 데이터를 가리키고 있는 형태입니다.
따라서 2차원 배열에서 clone() 메소드를 사용할 경우, 1차원 배열의 각 행의 참조만 복사됩니다
결과적으로 위와같이 데이터를 복사하는 것이 아닌
i번째 주소를 복사하는 얕은 복사가 일어납니다
따라서 깊은 복사를 하기 위해서는 반복문으로 각 행을 복사해야합니다.
3가지 방법으로 테스트했습니다 (출력코드 생략)
public class Main {
public static void main(String[] args){
int[][] test1 = {{1,2,3}, {4,5,6}, {7,8,9}};
int[][] test2 = new int[3][3];
int[][] test3 = new int[3][3];
int[][] test4 = new int[3][3];
for (int i = 0; i < test2.length; i++) {
for (int j = 0; j < test2[i].length; j++) {
test2[i][j] = test1[i][j];
}
}
for (int i = 0; i < test3.length; i++) {
test3[i] = test1[i].clone();
}
for (int i = 0; i < test4.length; i++) {
test4[i] = Arrays.copyOf(test1[i], test1[i].length);
}
test2[0][0] = 0;
test3[0][0] = -1;
test4[0][0] = -2;
}
}
2중 포문, 모든 1차원 배열 clone(), 그리고 Arrays.copyOf() 메소드를 사용했습니다
출력결과 깊은 복사가 이루어지는 것을 확인할 수 있습니다
2차원 배열을 복사할 때, 배열 객체를 그대로 clone할 경우 깊은복사가 아닌 얕은 복사가 일어납니다
문제의 원인은 데이터가 있는 영역인 2차원 배열 (만약 n차원 배열인 경우 n차원 배열)의 데이터가 복사되지 않고,
1차원 배열의 참조가 복사되기 떄문입니다
따라서 2차원 배열을 복사하려면 행을 각각 복사해야합니다.
방법으로는 이중포문으로 모든 인덱스에 일일이 복사하거나
1차원 행을 각각 clone 또는 Arrays.copyOf()를 사용해서 행의 데이터를 복사하는 방법이 있습니다