매일 매일 미라클 코딩
배열과 밀고 당기기: 깊은 복사, 얕은 복사 본문
배열 선언 시, 데이터를 메모리에 연속되게 저장합니다. 그리고 나서 자료형의 크기 만큼 (index) 번 이동하여 데이터를 사용합니다. 따라서 배열의 이름은 참조변수와 같습니다. C언어의 포인터 역할과 비슷합니다.
따라서 배열을 사용할 때는 배열의 참조값을 제어하는지 밸류값을 제어하는지에 주의하여 사용해야합니다.
그 차이점을 크게 배열의 얕은복사와 깊은복사 개념을 통해 알 수 있습니다.
1. 얕은 복사
배열을 단순히 '=' 연산자를 통해 복사할 경우, 원본 배열이 가리키는 참조값을 복사하게 됩니다.
그래서 아래와 같이 선언된 두 배열은 메모리 상의 같은 곳을 가리키게 되죠.
| int[] arr1 = new int[]{1, 2, 3, 4}; int[] arr2 = arr1; |
이때 복사한 배열2의 값을 바꾸게 되면
arr2[2] = 333;
System.out.println(java.util.Arrays.toString(arr1)); // 배열 값 출력하는 메소드
// [ 1, 2, 333, 4 ] 출력
arr2이 arr1의 레퍼런스(주소) 값을 복사했기 때문에, arr2가 가리키는 배열 값을 바꾸면
같은 곳을 가리키고 있는 arr1(원본)배열의 값 또한 바뀌는 것입니다.
그렇다고 배열 복사가 필요할 때마다, 하나씩 대입해줄 수는 없겠지요. 그럴 때 깊은 복사를 사용합니다.
2. 깊은 복사
물론 이미 알고 있는 '배열[인덱스] = 값;' 형식을 활용해 반복문을 돌려도 되겠습니다.
for(int i=0; i<arr1.length ; i++) {
arr2[i] = arr1[i]; }
하지만 처음부터 끝까지가 아닌 원본 배열의 일부만, 특정 인덱스부터 시작하고 싶다면?
이를 위해 자바 API에 간편하게 사용 가능한 기능이 이미 만들어져 있습니다.
1) arrayCopy
System.arraycopy(src, srcPos, dest, destPos, length);
원본 배열의 어느 지점부터 몇 개나 복사할지,
그리고 그것을 새로운 배열의 어느 인덱스부터 붙여넣을 지 값을 받는 메소드 입니다.
int[] arr1 = new int[] {1, 2, 3, 4};
int[] arr3 = new int[] {1, 2, 3, 4};
System.arraycopy(arr1, 1, arr3, 0, 2);
System.out.println(java.util.Arrays.toString(arr3));
위와 같은 경우 arr1의 인덱스 [1]부터 2개의 값을
arr3의 인덱스 [0] 부터 붙이라는 명령이 됩니다.
따라서 [2, 3, 3, 4] 가 출력됩니다.
2)arrays.copyOf
int[] arr2 = Arrays.copyOf(Original, Copy Length);
int[] arr1 = new int[] {1, 2, 3, 4};
int[] arr2 = new int[3];
arr2 = Arrays.copyOf(arr1, 2);
System. out. println(java.util. Arrays.toString(arr2));
기존배열에서 복사한 값으로 새로운 배열객체를 생성합니다.
원본 배열의 0번 인덱스 부터 Copy Length 만큼 복사합니다.
위와 같은 경우 arr2는 크기가 3인 배열로 선언되었지만,
Arrays.copyOf 함수를 통해 2개의 값을 가진 배열이 되었습니다. 따라서 [1, 2]가 출력됩니다.
참조값과 밸류값을 구분해야하는 배열의 특성을 고려해
조건문에서 비교연산을 할 때도 연산자에 주의해 코딩해야겠습니다.
조건문에서도 배열끼리 비교하는 경우 == 연산자를 통해서는 value 가 아닌 메모리상 주소값을 비교하게 됩니다.
위 arr1 과 같은 인자를 가진 arr3을 하나 더 선언해 주었습니다. ( int[] arr3 = {1, 2, 3, 4}; )
int[] arr3= new int[] {1,2,3,4};
if(arr1[0]==arr3[0]) {
System.out.println("두 배열이 같습니다.");
}
else
System.out.println("두 배열이 같지 않습니다.");
이 경우 "두 배열이 같지 않습니다." 라는 문자열이 출력됩니다.
이렇게 =, 또는 == 연산자로 배열을 비교할 때는, 밸류값이 아닌 레퍼런스를 비교하게 됩니다.
그렇다면 참조값이 아닌 배열의 내부 값을 비교할 땐 .equals() 메소드를 사용합니다.
if(arr1[0].equals(arr3[0])) {
System.out.println("두 배열이 같습니다.");
}
else
System.out.println("두 배열이 같지 않습니다.");
이렇게 조건문에서 equals 함수로 비교해주면 인덱스 0번 값이 둘다 1이므로 "두 배열이 같습니다."를 출력합니다.
배열처럼 참조값을 갖는 문자열, 객체를 비교할 때도 마찬가지 입니다.
입력받을 문자열이 특정된 경우 .equals() 를 사용해 비교해야 합니다.
그렇지 않으면 주소값만을 비교해 명령을 실행 못하는 불상사가 일어날 수 있습니다.
while (true) {
System.out.println("글을 저장하시겠습니까?");
ans = sc.nextLine();
if (ans.equals("y") || ans.equals("Y")) {
//if(ans=="y" || ans=="Y")로 쓸 경우 String의 참조값만 비교하기 때문에 false
System.out.println("글이 성공적으로 저장되었습니다.");
break;
}
}
배열의 깊은복사, 얕은복사.
그리고 참조값을 갖는 데이터 비교시 주의할 사항을 알아보았습니다.
'BackEnd > JAVA' 카테고리의 다른 글
| [JAVA] 상속과 생성자 (feat. super()) (0) | 2020.12.02 |
|---|---|
| 생성자(Constructor)의 MBTI 는 '엄격한관리자(ESTJ)' (0) | 2020.12.01 |
| [JAVA] 배열로 로또 번호 점지 받기 (중복 없는 랜덤 정수) (0) | 2020.11.17 |
| [JAVA] switch/case 문을 이용해 점심 메뉴 고르기 (feat. 랜덤함수 ) (0) | 2020.11.12 |
| 하나의 자바 파일에 여러 클래스 작성하기 (0) | 2020.11.12 |