<서론>

한동안 알고리즘을 쉬고 있었다. 사실 한창 할때만큼은 아니라도 하루에 간단한 문제 하나 정도는 풀려고 했었는데, 태생이 나태한 인간이라 정말 몇 개월을 알고리즘 문제 하나 안풀고 보냈다. 그러다 최근 코딩 테스트를 몇 번 보게 될 기회가 생겼는데, 오랜만에 해서 그런지 역시나 제대로 풀 지 못했다. 특히 배열을 파라메터로 사용할 때 얕은 복사 문제가 발생해서, 계속 오답이 나오는 경우가 있었다.

그래서 이 글을 통해 JAVA 배열의 얕은 복사와 깊은 복사에 관해 정리하려 한다.


 

 

<본론>

얕은 복사와 깊은 복사는 자료형이 참조형(Reference type)인 경우에 나뉜다. 기본형(Primitive type)에 경우 값 복사만 이뤄지기 때문에 얕은 복사인지 깊은 복사인지 고민할 필요가 없다.

그래서 참조형의 얕은 복사와 깊은 복사에 대해 알아보자.

 

1. 얕은 복사

얕은 복사는 해당 객체의 주소값을 복사하는 것이다. 결국 객체명은 다를 지언정 같은 값을 참조하는 것이다. 객체명은 다르지만 각 객체의 주소는 같다.

1
2
3
int[] orgArr = {1,2,3,4,5};
int[] copyArr = new int[5];
copyArr = orgArr;
cs

위와 같이 그저 "="로 표시하면 얕은 복사가 된다.

위 코드에서는 얕은 복사가 되었기 때문에 orgArr의 원소 값이 변경되면 copyArr의 값도 같이 변화한다.

orgArr과 copyArr의 주소 역시 동일하다.

1
2
3
4
5
6
7
8
9
10
class Test {
    public static void main(String[] args) {
        int[] test = {1,2,3,4,5}
        methodTest(test);
    }
    
    static void methodTest(int[] arr) {
        arr[1= 99;
    }
}
 
cs

어찌보면 당연한 것일 수도 있지만 위 코드처럼 메소드의 파라미터로 배열을 사용하고

해당 배열을 메소드 내에서 수정한다면 당연히 배열 값도 변경한다.

나는 이 부분에서 원하지 않는 수정이 발생했기 때문에 고생을 했다.

이 부분을 해결하기 메소드 내에 깊은 복사를 진행하고 복사한 배열을 변경하도록 메소드를 수정했다.

 

2. 깊은 복사

깊은 복사는 해당 객체의 값을 복사하는 것이다. 따라서 해당 값을 갖는 또다른 객체를 신규로 생성한다고 생각할 수도 있다. 당연히 각 객체의 주소는 다른 값이다.

1
2
3
4
5
int[] orgArr = {1,2,3,4,5}
int[] copyArr = new int[5];
for(int i=0; i<orgArr.length; i++) {
    copyArr[i] = orgArr[i];
}
 
cs

위 코드처럼 각 원소에 값을 직접 넣는 식으로 작성한다면 깊은 복사가 발생한다. 이 경우 orgArr이 변경되더라도 copyArr의 값은 변경되지 않는다.

 

1
2
3
4
5
6
int[] orgArr = {1,2,3,4,5}
int[] copyArr1 = new int[5];
int[] copyArr2 = new int[5];
 
copyArr1 = Arrays.copyOf(orgArr, orgArr.length);
System.arraycopy(orgArr,0,copyArr2,0,orgArr.length);
cs

반복문을 통해서 직접 값을 입력하는 방법 말고도 위 코드처럼 메소드를 사용하여 깊은 복사를 진행할 수도 있다. for문과 같은 반복문을 통해서 배열을 복사하는 것보다 위와 같은 코드로 배열을 복사하는 편이 좀 더 직관적인 것 같아서 위 방법을 더 사용한다.

https://stackoverflow.com/questions/18638743/is-it-better-to-use-system-arraycopy-than-a-for-loop-for-copying-arrays

 

Is it better to use System.arraycopy(...) than a for loop for copying arrays?

I want to create a new array of objects putting together two smaller arrays. They can't be null, but size may be 0. I can't chose between these two ways: are they equivalent or is one more effici...

stackoverflow.com

Stackoverflow 형님들 글을 보자면, 반복문을 통해서 배열을 복사하는 것보다, System.arraycopy나 Arrays.copyOf가 더 빠른 것을 알 수 있다. 읽기에도, 쓰기에도, 성능에도 더 좋기 때문에 힘들게 반복문으로 쓰지 말자.

 

위의 메소드들을 응용하여 2차원 배열도 깊은 복사를 할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
int[][] orgArr = new int[3][5];
int[][] copyArr = new int[3][5];
// orgArr 초기화
for(int i=0; i<orgArr.length; i++) {
    for(int j=0; j<orgArr[i].length; j++) {
        orgArr[i][j] = i*j;
    }
}
 
for(int i=0; i<orgArr.length; i++) {
    System.arraycopy(orgArr[i], 0, copyArr[i], 0, orgArr[i].length);
}
 
cs

위 코드처럼 배열을 원소로 갖는 배열이라고 생각하고, 각 원소를 깊은 복사하면 된다.


 

 

<결론>

원리를 안다면 정말 간단한 문제지만, 가끔씩 깜빡 잊고 넘어가는 부분이다. 이번에 코딩 테스트를 풀면서 이 문제 때문에 정말 아까운 시간만 많이 소비했었다. 평소 알고리즘 공부를 꾸준히 했으면 이런 문제는 일어나지 않았을 것이고, 혹시 똑같이 실수 했더라도 그 실수를 바로 잡는데 시간이 얼마 걸리지 않았을 것이다.

이번 코딩테스트를 하고 난 후 반성의 의미로 이 글을 정리했다. 앞으로는 실수하지 말자..

 

 

<참고자료>

https://dog-foot-story.tistory.com/27

 

[Java] 02. 배열의 복사(Arrays.copy)

배열 복사하는 방법 1. HashCode값 복사 (얕은 복사) scores의 해시코드 값이 scores1, scores2에 복사된다. 스택의 같은 공간에 접근함으로 힙에 담겨있는 값을 가져오게 된다. 따라서 기존 배열인 scores가 변경..

dog-foot-story.tistory.com

 

+ Recent posts