자바 배열복사 (Java Array Copy)
목차
자바 배열을 복사하는 방법을 알아보겠습니다.
자바에서 배열은 new를 사용한 힙메모리에 생성합니다. 힙메모리에 동적으로 생성한 데이터들은 참조변수로 접근할 수 있습니다. 배열의 문법을 보면 참조변수는 스택메모리에 생성되는데요.
C나 자바같은 정적 타이핑 언어(static typing)에서 유사한 방식을 사용하는 것은 우연이 아니라 같은 방식을 사용하는 것 입니다.
크기가 고정된 스택메모리에 기본형 변수 및 참조변수를 두고 실제값은 가변적 힙메모리에 자유롭게 생성하고 소멸시키도록 설계한 것입니다.
참조변수의 복사 개념부터 알아보겠습니다.
배열의 참조변수 복사
자바에서 new 키워드로 생성한 배열을 복사한다는 개념을 명확히 할 필요가 있습니다. 컴퓨터 프로그래밍에서 복사라는 말은 불완전한 단어입니다.
참조변수를 복사하는 것과 실제 배열의 값을 복사하는 것은 차이가 있습니다. 아래의 예제를 통해 알아보겠습니다.
public class Main { public static void main(String[] args) { int[] myArray = new int[] {1,3,5,7,9}; System.out.println("myArray = " + myArray); for (int i = 0; i < myArray.length; i++) { System.out.println("myArray[" + i + "]: " + myArray[i]); } int[] newArray = myArray; System.out.println("newArray = " + newArray); for (int i = 0; i < newArray.length; i++) { System.out.println("newArray[" + i + "]: " + newArray[i]); } } }
myArray = [I@7b23ec81 myArray[0]: 1 myArray[1]: 3 myArray[2]: 5 myArray[3]: 7 myArray[4]: 9 newArray = [I@7b23ec81 newArray[0]: 1 newArray[1]: 3 newArray[2]: 5 newArray[3]: 7 newArray[4]: 9
위의 코드는 참조변수를 복사한 예입니다. 보면 myArray와 newArray는 스택에서 참조변수가 같을 뿐 입니다. myArray와 newArray의 이름으로 출력한 경우 참조변수값이 똑같습니다.
참조변수가 같다는 것은 newArray나 myArray 어느쪽을 사용하여 요소의 값을 변경해도 실제값이 변경된다는 의미입니다. newArray[0] = 2; 는 myArray[0]의 값도 변경합니다.
아래의 이미지에서 보면 실제 값은 그대로 있고 참조변수가 손가락이라면 가리키는 손가락만 추가한 모습입니다. myArray 나 newArray 어느 쪽도 heap memory의 실제값에 접근하여 조작할 수 있습니다. 당연히 한쪽이 바꾸면 다른 한쪽도 바뀌겠죠.
System.arraycopy()함수
참조변수만 복사하는 것을 보통 얕은 복사(shallow copy)라고 합니다. 인스턴스까지 복사하는 것을 깊은 복사(deep copy)라고 합니다. 벌써부터 복잡하죠?
이 내용을 더 깊이 파고들면 컴파일로 수준의 이야기를 해야하기 때문에 입문 과정에서는 과도합니다. 타이틀을 입문강좌라고 붙인 것은 최대한 입문수준을 벗어나지 않기 위해서이죠.
아래 그림을 위의 이미지와 비교해서 보면 더 명확합니다.
myArray 와 cpArray 가 같은 인스턴스를 가리키고 있었다면 Heap 1과 같은 실제 인스턴스를 하나 더 만들어서 cpArray에 할당한다면 둘은 각각 독립된 인스턴스를 가질 수 있습니다.
배열을 하나씩 복사하는 것 정도는 이제 만들 수 있을겁니다. 하지만 라이브러리를 이용하는게 여러 측면에서 유리하니 자바가 기본 패키지에 구현한 System.arraycopy 함수를 쓰도록 합니다.
복사할 함수의 시작점, 할당되는 함수의 시작점, 복사가 끝나는 부분을 인수로 넘기면 됩니다.
public class Main { public static void main(String[] args) { int[] myArray = new int[] {1,3,5,7,9}; int[] cpArray = new int[7]; System.arraycopy(myArray, 0, cpArray, 0, myArray.length); System.out.println("myArray = " + myArray); for (int i = 0; i < myArray.length; i++) { System.out.println("myArray[" + i + "]: " + myArray[i]); } System.out.println("cpArray = " + cpArray); for (int i = 0; i < cpArray.length; i++) { System.out.println("cpArray[" + i + "]: " + cpArray[i]); } } }
myArray = [I@7b23ec81 myArray[0]: 1 myArray[1]: 3 myArray[2]: 5 myArray[3]: 7 myArray[4]: 9 cpArray = [I@6e8cf4c6 cpArray[0]: 1 cpArray[1]: 3 cpArray[2]: 5 cpArray[3]: 7 cpArray[4]: 9 cpArray[5]: 0 cpArray[6]: 0
참조변수의 값이 다르다는 것이 보입니다. 또 결과창에서 보는 것 처럼 복사가 끝나고 공간이 좀 남을 수도 있습니다. 어떤 개념인지 알겠죠?
복사가 이루어지기 전에 cpArray에 new를 사용해서 메모리를 받아놓지 않으면 오류가 납니다.
java: variable cpArray might not have been initialized
cpArray가 초기화 되지 않은 것 같다. 즉 메모리를 사용할 수 없는 상태라는 말 입니다.
요약
자바 배열복사에 대해 알아봤습니다. 사실 얕은 복사와 깊은 복사는 프로그래밍 언어마다 약간씩의 차이를 보입니다.
초보자가 이 내용을 한번에 이해하는 것은 좀 어렵습니다. 중급자 이상도 이 개념을 파고들면 머리에 쥐가 날 것입니다. 여기서 더 깊이 들어가려면 컴파일러 그 자체의 작동원리를 깊게 파야하기 때문에 입문과정에는 적절하지 않습니다.
그럼에도 이 주제를 다룬 것은 객체 지향프로그래밍에서 참조변수를 제대로 알고 사용하는 것이 중요하기 때문입니다.
입문강좌의 레슨이 벌써 20화에 도달했습니다. 입문에서 시작하여 여기까지 제대로 학습을 마쳤다면 상당한 시간을 투자했을 것입니다. 최대한 핵심 내용으로 구성했지만 1화의 실습에 최소 10분 정도 들어간다고 했을 때도 200분 분량입니다.
3시간 이면 유튜브에서 튜토리얼 한과목 정도 됩니다. 속성코스 튜토리얼에는 이론 설명을 최소화하고 그냥 따라하기 위주니까 조금 더 압축이 되죠.
물론 이 자바 입문강좌가 몇 화까지 갈지는 모르겠습니다. 현재 속도면 20화가 아니라 50화도 갈 것 같은데 저도 어느 시점에서 퉁쳐야 할지 아직 모르겠습니다.
객체 지향에 대한 내용은 아직 시작도 안했다니 조금 더 진행될 것 같습니다.
이 포스팅을 읽는 분들도 스스로 진도 체크를 하면서 학습목적을 예정되로 달성하실 수 있기를 바랍니다.
외부참조문서
자바 배열복사 Java.lang.System.arraycopy() Method – Tutorialspoint
자바 배열복사 함수 System (Java Platform SE 7 ) (oracle.com)