얕은 복사와 깊은 복사에 대해서는 C언어를 공부할 때부터 중요한 개념이라고 알고 있었다.
알고리즘 문제를 풀면서 깊은 복사를 할 일이 자주 있는데, 헷갈리는 부분이 있어 정리가 필요했다.
배열을 복사하는 clone() 함수가 일차원 배열에서는 깊은복사가 되지만, 또 이차원 배열에서는 얕은복사가 되고, Collection의 생성자를 통한 복사는 깊은 복사이지만, clone() 함수는 얕은 복사가 되는 등등.. 정리를 한번 해두어야 겠다고 생각했다.
얕은 복사
원본 객체와 복사본 객체가 같은 객체를 참조하는 경우를 얕은 복사라고 한다. 얕은 복사를 수행하면 원본 객체의 변경이 복사본 객체에도 영향을 미친다. 따라서, 원본 객체와 복사본 객체가 독립적인 객체를 유지하려면 깊은 복사를 수행해야 한다.
int[] arr1 = {1, 2, 3};
int[] arr2 = arr1; // 얕은 복사
int[] arr1 = {1, 2, 3};
int[] arr2 = new int[arr1.length];
System.arraycopy(arr1, 0, arr2, 0, arr1.length); // 얕은 복사
ArrayList<Integer> list1 = new ArrayList<>(Arrays.asList(1, 2, 3));
ArrayList<Integer> list2 = (ArrayList<Integer>) list1.clone(); // 얕은 복사
ArrayList<Integer> list1 = new ArrayList<>(Arrays.asList(1, 2, 3));
ArrayList<Integer> list2 = new ArrayList<>(list1.subList(0, list1.size())); // 얕은 복사
깉은 복사
원본 객체와 복사본 객체가 서로 다른 객체를 참조하는 경우를 깊은 복사라고 한다. 깊은 복사를 수행하면 복사된 객체는 원본 객체와 독립적인 객체가 된다. 이를 위해 복사할 객체의 모든 필드를 복사하여 새로운 객체를 생성해야 한다.
int[] originalArray = {1, 2, 3};
int[] copiedArray = new int[originalArray.length];
for (int i = 0; i < originalArray.length; i++) {
copiedArray[i] = originalArray[i];
}
int[] originalArray = {1, 2, 3};
int[] copiedArray = Arrays.copyOf(originalArray, originalArray.length);
List<Integer> originalList = Arrays.asList(1, 2, 3);
List<Integer> copiedList = new ArrayList<Integer>(originalList.size());
for (Integer i : originalList) {
copiedList.add(i);
}
List<Integer> originalList = Arrays.asList(1, 2, 3);
List<Integer> copiedList = new ArrayList<Integer>(originalList.size());
Collections.copy(copiedList, originalList);
ArrayList<Integer> originalList = new ArrayList<>(Arrays.asList(1, 2, 3));
ArrayList<Integer> copiedList = new ArrayList<>(originalList);
배열의 clone() 함수
일차원 배열의 경우, 새로운 배열이 생성되고 그 안에 원래 배열의 요소들이 복사된다. 이 경우에는 깊은 복사가 이루어진다.
하지만 이차원 배열의 경우, clone() 메소드를 호출하더라도 새로운 2차원 배열이 생성되지 않는다. 대신에, 새로운 배열의 각 요소는 원래 배열의 같은 인덱스에 해당하는 요소와 같은 참조를 가리키게 된다. 따라서, 이차원 배열의 clone() 메소드는 얕은 복사가 이루어지게 된다.
int[][] originalArray = { { 1, 2 }, { 3, 4 } };
int[][] copiedArray = originalArray.clone();
이 경우, originalArray와 copiedArray는 다른 객체이지만, originalArray[0]과 copiedArray[0]는 같은 객체를 참조하게 된다. 따라서, 만약 originalArray[0]의 값을 변경하면, copiedArray[0]의 값도 변경된다. 이러한 이유로 이차원 배열의 경우에는 clone() 메소드를 사용하여 깊은 복사를 하지 않는 것이 좋다. 대신에, Arrays.copyOf()과 같은 다른 메소드를 사용하여 깊은 복사를 수행해야 한다.
이렇게 자바에서 얕은 복사과 깊은 복사의 의미와, 배열과 컬렉션에서 각각 복사할 수 있는 방법에 대해 알아보았다.
서로 다른 독립된 객체를 만들어야 하는데, 자칫 잘못 사용하면 얕은 복사가 되어 원하는 결과를 얻지 못할 수 있기 때문에, 이 부분은 제대로 알고 넘어갈 필요가 있다.